Обновить

Комментарии 191

ЗакрепленныеЗакреплённые комментарии

Спасибо всем за конструктивную критику. Внёс несколько улучшений в формат, чтобы закрыть больше кейсов использования:

  • ASCII символы из "быстрого набора" теперь всегда кодируются одинаково независимо от режима, что даёт детерминизм представления символов, а следовательно и упрощает поиск по байтам.

  • Старшие байты вообще и преключатели режимов в частности теперь используют дифференциальное кодирование относительно последнего режима. Это позволяет итерироваться по строке в обе стороны. Так же пришлось в 2 раза уменьшить размер широких страниц (и соответственно удвоить их число), чтобы избежать неоднозначности при итерировании в обратном направлении.

  • Строка теперь не только начинается с ASCII режима, но и должна заканчиваться в нём. Это не только позволяет итерироваться с конца, но и делает конкатенацию совсем тривиальной задачей. Цена этому - возможное появление 1 доп байта в конце.

  • В ASCII режиме теперь доступна 21 штука однобайтовой диакритики без переключения режима. Для этого, конечно, нужен ещё правильный ввод текста или предварительная его NFD нормализация. Но это всё же лучше, чем ничего.

Код в итоге несколько усложнился и замедлился, что мне совсем не травится. Буду рад, если подкинете идеи, как его можно было бы упростить и ускорить.

решать вопрос битых данных надо не на уровне кодирования текста, а на транспортном уровне, чтобы он гарантировал точную доставку, любых данных. Даже не текстовых.
В таблице символов Юникода чуть более миллиона позиций, для кодирования которых хватило бы и 20 бит (что не более 3 байт). Но большая часть из них до сих пор не используется, так что пару бит можно смело выкидывать на мороз.

Отбойники на дорогах довольно редко вступают в дело, но их почему-то всё ещё ставят вместо перекладывания всей ответственности на водителя.

А водителя в случае смертельного ДТП тоже можно перекомпилировать или отменить через ctrl-z?

Одни и те же мемы из раза в раз. Вот нет бы что-то новое придумать уже.

Непонятно, какую задачу решает предложенный формат. Если компактизация текстовых данных для передачи, то можно использовать форматы ZIP или 7Z. Если известен язык текста и нужно ещё сильнее сжать, можно предварительно преобразовать в нужную ANSI-кодировку, и потом 7Z.

Человечество прошло этот этап, и лучше и универсальнее Unicode для обработки текста всё равно не придумать. Хотя вот LLM'ки кормят не буквами, а токенами - вот тут есть простор для оптимизации кодировки под разные языки и применения.

Нет цели сжать любой ценой. Есть цель бинаризовать так, чтобы сжатие потом не требовалось. К тому же ZIP мало того, что существенно медленнее и сложнее, так ещё и ничего не даст на множестве коротких текстов. А Unicode вроде никто не отменял пока кроме китайцев.

Есть цель бинаризовать так, чтобы сжатие потом не требовалось.

https://habr.com/kek/v2/articles/?period=daily&sort=date&fl=en%2Cru&hl=ru&page=1&perPage=10
Size 41 Кб
Transferred 10.70 Кб

Я взял этот текст, перевел в UCF и сжал ZIP.

Text length: 41109
UCF  length: 33605
Text gzip level 3: 10471
UCF  gzip level 3: 9322
Text gzip level 9: 10022
UCF  gzip level 9: 9120

Даже в вашей кодировке сжатие все равно дает значительное уменьшение размера. То есть свою цель вы не достигли.

На 3 уровне сжатия получилась разница 1149 байт, на 9 уровне 902 байта. Для сжатого размера разница 10 процентов.

https://habrastorage.org/r/w1560/getpro/habr/upload_files/db0/84f/9bd/db084f9bd52c89eb8163135cfb686fce.png

А одна вот эта картинка занимает 66617 байт. Даже по сравнению с ней разница загружаемых данных получается около 1 процента. А если все картинки посчитать, то еще меньше.

Раз вас эта тема так беспокоит, то возьмите, например, этот токенизатор, разбейте сей текст на токены, и каждый токен независимо сожмите в Zip, Brotli или что там сейчас самое модное. Очень жду столь же впечатляющих результатов. Только не забудьте ещё и время замерить. Если лень разбираться с $mol_regexp, то вот результат компиляции:

/(?:((?:((?:((?:\r){0,1}\n)|(\r)))|((?:\t){1,})|(\p{Extended_Pictographic}(?:\p{Emoji_Modifier}){0,1}(?:\p{Emoji_Component}\p{Extended_Pictographic}(?:\p{Emoji_Modifier}){0,1}){0,})|(\b(https?:\/\/[^\s,.;:!?")]+(?:[,.;:!?")][^\s,.;:!?")]+)+))|((?:[ \u{a0}]){0,1}(?:[\p{General_Category=Uppercase_Letter}\p{Diacritic}\p{General_Category=Number}]){1,}(?:[\p{General_Category=Lowercase_Letter}\p{Diacritic}\p{General_Category=Number}]){0,})|((?:[ \u{a0}]){0,1}(?:[\p{General_Category=Lowercase_Letter}\p{Diacritic}\p{General_Category=Number}]){1,})|((?!(?:((?:\r){0,1}\n)|(\r)))(?:\p{White_Space}){1,}(?=\p{White_Space}))|((?!(?:((?:\r){0,1}\n)|(\r)))\p{White_Space}(?!\p{White_Space}\p{General_Category=Uppercase_Letter}\p{General_Category=Lowercase_Letter}\p{Diacritic}\p{General_Category=Number}))|((?:[ \u{a0}]){0,1}(?:[^\p{General_Category=Uppercase_Letter}\p{General_Category=Lowercase_Letter}\p{Diacritic}\p{General_Category=Number}\p{White_Space}]){1,}))))/gsu

Зачем мне сжимать отдельно каждый токен в ZIP, если для всего текста он сжимает лучше? Что это должно доказать? Я говорю про стандартное сжатие, которое делают сервер и браузер при передаче данных.

Проверил с этим regexp и JSON по ссылке выше, выигрыш от ZIP по сравнению с просто JSON.stringify(tokens) начинается уже от 8 токенов. Как сюда встроить $mol_charset_ucf_encode для каждого токена и использовать результат для передачи по сети я не разобрался. Хотите проверить, приводите конкретный код тестирования, который вы себе представляете.

Только не забудьте ещё и время замерить.

Если вы будете передавать 33 килобайта в UCF без ZIP по сравнению с 10 килобайт в UTF8 с ZIP, то передача дополнительных сетевых пакетов займет значительно больше времени, чем вы сэкономите на отсутствии ZIP.

Охотно верю, что если вы передаете по сети по одному слову в запросе, то ваша кодировка будет меньше на несколько байт. Только обычно так никто не делает, и на масштабах длины одного слова это тем более незначительно.

Возможно затем, что есть домены, в которых нет удобного одного большого текста, а есть лишь неудобное множество мелких. От 1 до 15 символов как правило, каждый со своими метаданными для бесконфликтного слияния.

Что вы там проверили я так и не понял. Но время, разумеется, интересовало кодирования/декодирования, а не передачи. А передача одного токена - самый частый кейс, да.

"Множество мелких" для передачи по сети сериализуется в JSON или Protobuf, и получается "один большой текст", который сжимается ZIP.

Я понял, что вас интересовало время кодирования/декодирования, я сказал, что на фоне времени передачи данных разница выглядит незначительно. Если вы быстрее кодируете, но получаете больше данных для передачи, то будет быстрее передавать 1 пакет в ZIP, чем 2 пакета в вашей кодировке без ZIP.

"Cо своими метаданными" означает, что у вас кроме самого текста передается какая-то структура. Но фоне которой экономия нескольких байт выглядит еще меньше. А еще есть заголовок TCP-пакета 20 байт. А если вы передаете по HTTP, то еще и заголовки HTTP. Поэтому просто передача в одном запросе 2 токенов вместо 1 даст большую экономию по размеру и времени обработки, чем специальная кодировка текста.

Для передачи одного токена по сети ваша кодировка может и является подходящим решением, но это недостаточная причина, чтобы использовать ее вместо UTF-8, как вы предлагаете в статье.

Передача по сети происходит как правило в фоне и особого значения не имеет. А вот что имеет - скорость поднятия данных из локального хранилища.

У всех токенов разный жизненный цикл. Каждый и них независимо хешируется, подписывается цифровой подписью и шифруется, что даёт выравнивание по 16 байт. Это значит, что типичное русское слово из 10 букв может занимать 16 или 32 байта в зависимости от кодировки. По сравнению с 40 байтами метаданных это 30% разницы. И выбор правильной кодировки даёт тут буквально бесплатную экономию.

До шифрования сжимать нет смысла из-за малой длины строк, а после шифрования - тем более из-за энтропии. При синхронизации отбираются лишь те юниты, которых нет у другого пира, и собираются в пакет. Тут уже к пакету может быть применено сжатие, которое сожмёт повторяющиеся в разных юнитах метаданные, но не сильно.

С вашим подходом не получится символ из потока считать произвольно, нужно будет пройтись по потоку обратно (если есть такая возможность), чтобы найти, к какой таблице он относится. А если часть данных повреждена — внезапно весь текст становится нечитаемым, придётся перебирать все таблицы, проверяя его на осмысленность. Вы именно алгоритм сжатия придумали, и у него есть свои минусы.

Если "произвольно" - это по индексу, то этого не позволяет ни одна кодировка с переменным числом байт на символ. Если же "произвольно" - это по заранее найденной позиции, то мы уже прошлись до неё от начала.

А если часть данных повреждена, то у них не сойдётся чек-сумма, отвалится проверка хеша, обломается валидация формата, да и вообще данные не смогут быть расшифрованы. Проще говоря, не помню, когда последний раз до меня доходил повреждённый текст, да ещё и настолько уникальноважный, чтобы собирать его по крупицам.

полностью поддерживаю, но есть много идей по улучшению, сам над этим размышлял, но не оформлял и не записывал

> Так как символы из разных языков могут смешиваться как угодно, то переключение режима должно иметь минимальный размер и укладываться в 1 байт. Соответственно, число возможных режимов сильно ограничено, деля свои коды с кодами символов.

Это сразу неправильно, наш идеологически выверенный православный юникод должен быть спроектирован так, чтобы число возможных режимов и число возможных символов можно было расширять без ограничений и противоречий, сохраняя совместимость. Программа, не знакомая с какой-то подкодировкой может её просто отобразить какими-то кубиками, но продолжить декодирование строки дальше.

Единственное о чём следует договориться, это что в начале идёт код подкодировки, который должен быть переменной длины. Этот номер подкодировки кодируется в стиле varint, то есть значения до 128 помещаются в 1 байт, от 128 до 16K - в 2 байта и т.п. Таким образом мы можем всегда добавить ещё одну подкодировку. Ещё в номере можно зарезервировать один бит для различения 0-терминируемых строк и строк с длиной строки, может ещё один для чего-то другого. Соответственно, самый популярные кодировки будут занимать 1 байт, менее популярные и более поздние -2 байта, а когда-то может дойдём и до 4х.

Нуль-терминируемые строки используют 0 как индикатор переключения кодировки и два 0 (00) для индикации конца строки. За 0 всегда следует либо 0 (конец строки), либо код подкодировки, которая может быть и однобайтовой и двухбайтовой и в принципе какой угодно.

При этом подходе мы можем допустить строки и без 0-терминации, с длиной строки в начале. За это может отвечать один бит в номере кодировки.

Дополнительно можно в некоторых случаях обязать повторять подкодировку после символа перевода строки и/или точки. Это может быть полезно при чтении больших файлов.


Например, английский текст будет вполне читабелен любым latin1- или юникод- просмотрщиком, но будет иметь в начале пару "мусорных" байт.

При этом важно выделить страницу для каждого языка, чтобы мы знали не только кодировку, но и язык подстроки.

Можно и сам utf-8 иметь в виде подкодировки при этом подходе.

Необходимость поддерживать тысячи кодировок - капитально усложнит и замедлит реализацию без особого выигрыша в размере.

Выигрыш примерно такой же, как у автора - в 2 раза для кириллицы, например. Но по-моему основной недостаток юникода, на мой взгляд, не в избыточности, а в том что он не предоставляет информацию о языке. То есть кириллический текст, например, может быть русским, болгарским, украинским. Латинский - английским, немецким, итальянский и т.п.

Это неверно и является основным недостатком юникода. Важна информация о языке!

Я сравнивал с UCF, разумеется. Так что разница будет только на специфичных буквах, не попадающих в страницу с основными. Тут специфичная кодировка могла бы помочь. Но какой ценой..

Важно оставить систему открытой для расширения в будущем. Одного байта будет мало даже на сегодняшний день.

> Согласно данным крупнейшего в мире каталога языков Ethnologue, по состоянию на 2024 год на Земле насчитывается 7164 языка

https://ru.wikipedia.org/wiki/Языки_мира

И языки не стоят на месте, будут развиваться. Если кодировать язык в строке (а это правильно и полезно), то одним байтом уже сегодня не обойдёшься. А в будущем их будет больше.

Это же относится и к самим языкам - они развиваются и меняются и неизбежно алфавиты будут меняться.

"Благо в байте не 7 бит, а 8".
ну в те времена, а точнее до 70х, в байте не всегда было 8 бит. были и 6-ти битные байты (IBM 704, PDP‑1) и 7ми битные.

А в военное время количество байтов доходило до 9

В обычных байтах по 8 бит, но в каждом четвертом байте - 9. Потому что каждый четвертый байт - високосный.

Это юлианские байты

Ненене, это как раз григорианские байты на самом деле. С григорианскими байтами все просто - каждый четвертый високосный.

С юлианскими байтами немного сложнее:

В первых 36 юлианских байтах високосными являются каждый третий байт. Но первый юлианский байт сразу високосный (но это не точно - см. ниже).

Потом следующие 16 байт - все подряд невисокосные.

И только начиная с 52 байта високосными являются каждый четвертый.

Причем существует две реализации юлианских байтов - в одной отсчет високосных байтов начинается с первого байта, а в другой - со второго. Точнее, реализаций на самом деле больше двух, но эти две наиболее распространены. Реализации несовместимы между собой, однако по причине наступления EOL для них всех это уже не имеет никакого значения.

Стандарт на размер юлианских байтов оказался слишком сложен в реализации и поэтому продержался совсем недолго - всего каких-то 1600 лет - и был заменен григорианскими байтами. В процессе замены 11 байт были безвозвратно потеряны, но так как это случилось больше 400 лет назад, то их уже не спасти. И бэкапов нет. Ну или нам всем врут, что их якобы нет.

То есть я понимаю, что это звучит дико, но тем не менее.

Но ведь в григорианских, если номер байта делится на 100, то он не високосный, кроме случая, когда он делится ещё и на 400.

Но это все равно проще, чем девять несовместимых между собой стандартов юлианских байт.

НЛО прилетело и опубликовало эту надпись здесь

9 бит в байте это ECC. :)

я публикую эту статью в надежде, что вы поможете подсветить все возможные косяки

<перед этим>

я планирую внедрить UCF в свой формат бинаризации произвольных данных VaryPack, который используется в моей децентрализованной базе реального времени Giper Baza, у которой есть все задатки стать основой Web4

Вам уже подсвечивался самый главный косяк, который называется Трагедия общин. Из-за этого косяка ваш Web4 является сказочной страной феечек, а без Web4 у вас не получится всё остальное.

А весь Open Source спонсирует ЦРУ, да.

Если завтра бюджет Canonical сократится в 2 раза, жёсткий диск на вашей Ubuntu не уменьшится пропорционально.

Если послезавтра Canonical сообщит, что для покрытия бюджетной дыры ей надо ввести рекламу, на вашей установочной флешке всё ещё останется дистрибутив без рекламы.

А если вы честно спроецируете данные гипотетические события на ваши мечтания, то поймёте их принципиальное отличие от OpenSource.

Люди, которые сами ни на что не способны, очень любят рассказывать другим, что у них ничего не получится, вместо того, чтобы направить эту энергию в более полезное русло.

То, что я плохой-нехороший и у вас с этого бомбит - понятно. Но тут дело не в мотивации моих вопросов, а в аргументации ваших ответов.

Исходники условной Ubuntu есть смысл держать у себя локально. Не зависимо от того, есть ли у Canonical деньги на их собственные серверы или нет. Просто потому, что условная Ubuntu самоценна.

Держать чужие дикпики потому, что "децентрализованная интернет-свобода", а у автора дикпиков нет своего компа - ну как бы, а зачем?

У вас на этот вопрос нет аргументированного ответа - лишь заезженное "вы просто завидуете, сперва добейтесь, революция победит".

Я с вами по-взрослому, а вы на фальцет переходите.

Насколько я понимаю основные плюсы по сравнению с utf-8 это
1. Более компактное представление
2. Скорость кодирования/декодирования
При используя архиватор получим ещё более компактное представление. А декодирование там будет точно также шустро работать. Да и кодирование будет не сильно тормозным.
Т.е. "плюсы" именно такой кодировки выглядят сомнительно

Неужели такая простая мысль, что любой дополнительный пре/пост-процессинг не может быть бесплатным, слишком сложна для современного поколения разработчиков?

Неужели такая простая мысль, что любой дополнительный пре/пост-процессинг не может быть бесплатным, слишком сложна для современного поколения разработчиков?

Странновато это слышать от человека, мечтающего о бесплатных серверных мощностях :)))))

Про возможность безопасно грохать базу на проде я даже не рассказываю, а то ещё помрёте от кринжа.

Отгавк не засчитан.

Так вы то предлагаете делать этот пре/пост-процессинг ВСЕГДА. Т.е. выполнять всегда не бесплатную операцию.
Я же говорю о том, что если всегда такая операция нужна, то имеет смысл использовать что-то боле специализированное для упаковки/распаковки. А если не всегда нужна, то нет смысла выполнять трансформацию.

Я не предлагал никаких операций в дополнение к utf8 кодированию. Я предложил одно кодирование заменить на другое сопоставимой сложности, но при этом большей эффективности.

В том то и дело что вы хотите одно кодирование заменить на другое кодирование. При этом есть варианты более эффективные чем ваш. О чем вам и написали указав на тот же zip
Т.е. как идея, ваш вариант хороший. Но на практике нет никаких предпосылок для него.

Так ваш вариант кодирования по сути требует безальтернативного перекодирования в более удобное представление и обратно для использования внутри приложения, иначе все манипуляции со строками становятся алгоритмически сложнее. Тогда как UTF-8 в большинстве случаев можно использовать as is во всем приложении от диска и сети до преобразования и вывода.

Именно UCF означает постоянный пре/пост-процессинг на границе компонент.

Как есть utf8 тоже алгоритмически не так уж просто использовать. Родная кодировка Windows, MacOS, Java, JS - UTF-16.

Любую UTF строку элементарно алгоритмически использовать, там отличие только в размере элемента массива.

Чего в мире больше, UTF-8 или UTF-16, вопрос дискуссионный, но скорее академический.

Суть конечно в том, что UTF возможно придется конвертировать. UCF совершенно точно обязательно надо конвертировать, причем скорее всего в тот же самый UTF.

На Windows и MacOS совершенно точно придётся конвертировать и UTF-8. А в браузере на Linux вы получите ещё и двойную конвертацию туда-сюда. Вас это почему не смущает?

  1. Слишком хрупко: повреждение кода переключения алфавита ломает не единственный символ, как в UTF-8, а весь блок символов до следующего кода переключения алфавита.

  2. Значения типа char и array of char. Да, для пользователя JavaScript или Python проблема может быть неочевидна, т.к. в этих языках есть только тип string. Но в компилируемых языках программирования есть не только строки, но и отдельные символы, и массивы символов, не тождественные строкам. И для них ваши оценки экономии объёма не имеют смысла, т.к. каждый символ, включая ASCII, придётся кодировать минимум 2 значениями (код алфавита и код самого символа в алфавите), что может привести не к сокращению, а к раздуванию объёма данных.

  3. Операции со строками - от сравнения до регулярных выражений. Во многих из них придётся вводить дополнительные накладные расходы. Даже в банальной конкатенации строк придётся либо вводить дополнительные проверки для корректной вставки кодов переключения алфавитов на границах склеиваемых строк, либо смириться с забиванием строк заведомо лишними кодами переключения алфавита, съедающими экономию места и замедляющими сравнение строк. Вы оценивали только кодирование/декодирование, но никак не оценивали усложнение работы с содержимым строк при использовании вашей кодировкой.

  4. Бессмысленность экономии на спичках. Экономия на символах имела смысл во времена PDP-11 c 56 Kb RAM, но при современных объёмах оперативной и внешней памяти - зачем??? Остаётся только передача по сети. Но на малых объёмах данных такая экономия не даст заметного выигрыша (объём служебной информации передаваемого по сети пакета данных никак не уменьшается). А при больших объёмах стандартизированные много лет назад механизмы сжатия трафика (встроенные и в серверы, и в браузеры, и в библиотеки, используемые в языках программирования; и нет - это не zip, так что претензии к скорости работы не принимаются) обеспечат несравнимо лучшее сжатие текстов, чем ваша кодировка.

P.S. Если же вы собираетесь использовать свою кодировку только для чтения/записи данных, а внутри кода использовать другую кодировку, то это тем более лишено смысла, т.к. вы лишь раздуваете объём используемой кодом оперативной памяти.

  1. В современных реалиях данные передаются в структурированном виде. Так что повреждение одного байта - это уже не "ой, ну пользователь сам поправит если надо", а "данные не могут быть прочитаны". Даже если это, казалось бы, текстовый JSON.

  2. Хз о чём речь. Внутреннее представление в программе может быть любое. И любая кодировка потребует конвертации во внутреннее представление и обратно. В некоторых случаях, типа Go или Rust, внутреннее представление в UTF8, тогда чтение строки в той же кодировке позволяет ограничиться лишь валидацией. Но в общем случае всё не так радужно. В JS, как видите, приходится перегонять в UCS-2 и обратно даже UTF8.

  3. Проверка там не сложная. Достаточно хранить вместе со строкой её последний режим.

  4. От того, что сжатие куда-то там встроено, оно не становится ни невесомым, ни мгновенным. Сам я наблюдал, например, утечки памяти в вебсокетах при включении сжатия. После шифрования сжатие бесполезно. И до шифрования в случае маленьких строк тоже. Магическое мышление в духе "компрессор как-нибудь сам разберётся" тут не работает.

Напишете пулл-реквесты во все стандартные библиотеки мира, чтобы адаптировать concat и substring под вашу кодировку? :)

Поддержка новых типов строк, как и любых других типов, добавляется совсем не так, как вы думаете.

Я на 4 отвечу. Я уже написал здесь, но мысль важна, я её повторю.

Основной недостаток юникода, на мой взгляд, не в избыточности, а в том что он не предоставляет информацию о языке. То есть кириллическая строка, например, может быть русской, болгарской, украинской. Латинская - английской, немецкой, итальянской и т.п.

Вот это является основным недостатком юникода. Так что новый юникод, с информацией о языке нужен! Можно, конечно, теоретически, продублировать латинский алфавит для итальянского, немецкого, французского и т.п. В принципе юникод позволяет расширение. Но это, на мой взгляд, будет слишком расточительно. Поэтому идея хранить какую-то дополнительную информацию о строке в самой строке, в принципе верна и перспективна.

Но у юникода нет такой задачи, и не очень понятно, почему она должна быть. В своем же ПО вы всегда можете иметь структуру данных, где у вас есть и строка, и любые метаданные.

Язык это же не отделимое свойство, которое можно применить к тексту, типа шрифта, цвета или отступов, это неотъемлемое и неотделимое свойство любого текста, слова. Каждый текст написан на каком-то языке. Почему и зачем это должно храниться где-то отдельно? По-моему очень странная идея.

Строка символов — это более низкий уровень абстракции, чем текст. Текст может быть репрезентирован в виде строки символов, но строка символов не обязательно представляет собой текст, например, это может быть произвольный набор букв или ASCII art. У текста же есть язык, грамматика, смысл, авторские права и еще множество вещей, которые относятся к тексту, но не относятся к произвольной строке символов.

Текст может быть репрезентирован в виде строки символов, но строка символов не обязательно представляет собой текст, например, это может быть произвольный набор букв или ASCII art.

Произвольный набор букв (имелось в виду случайный набор?) и ASCII art не требуют языка, здесь я согласен. Но нам ничего не мешает приписать им и какой-то произвольный язык...

> У текста же есть язык, грамматика, смысл, авторские права и еще множество вещей, которые относятся к тексту, но не относятся к произвольной строке символов.

Язык вполне относится. Если мы используем строку для хранения SHA256 в BASE64, то нет, но это уже скорее набор байт, а не текст. Но любая строка текста написана на каком-то языке.

Но нам ничего не мешает приписать им и какой-то произвольный язык...

А потом, внезапно, две строки с одинаковым текстом у вас оказываются не равны.

Вы обошли ключевое понятие — уровни абстракции.

  1. Самый низкий: массив байтов. Он может обозначать символы, может бинарные данные. На этом уровне намеренно не задается семантика хранимых значений.

  2. Более высокий: набор символов. Один и тот же символ ("ъ") может использоваться в разных языках, может вне языка, может хоть в роли части орнамента. На этом уровне намеренно не затрагиваются такие понятия как слово, текст, язык. Unicode задает только набор символов.

  3. Еще более высокий — состоящий из символов текст, по отношению к нему уже можно применить понятия язык и слово.

Из того, что unicode является только набором символов, и намеренно создан, чтобы работать именно на этом уровне, не затрагивая другие, следует то, что он не определяет и не должен определять, как интерпретировать эти символы в тексте.

Давайте представим, что у нас уровни 2 и 3 смешались.

Вот текст:

fn main() {
    println!("Привет!");
}

На каком он языке? Может показаться, что на английском, но нет, в английском нет таких знаков препинания и слова fn.

Ок, припишем ему язык Rust, как есть. И вдруг возникает куча вопросов:

  • Слово "привет" — это еще Rust или уже русский язык? Или надо приписать ему два кода языка?

  • А это точно русский, может быть другой славянский язык?

  • А кавычки — это русский язык или Rust?

  • А как терминал должен работать при выводе текстов на разных языках?

  • А как в текстовом редакторе отметить, что это русский язык?

  • И как вообще программы должны отображать, на каком языке написан фрагмент текста, и зачем им нужно это делать?

  • А если человек в мессенджере ввел "Привет", и мы получили это в нашей программе через API, то это на каком языке? А если он в следующим сообщением в этом же мессенджере отправил "добры дзень", то это на каком?

Все эти вопросы — следствие того, что мы попытались применить параметры, свойственные тексту, к уровню символов, где они неприменимы, и где нам даже неоткуда значения этих параметров получить.

А потом, внезапно, две строки с одинаковым текстом у вас оказываются не равны.

Имеется в виду, две строки с одинаковым начертанием? В юникоде ровно точно такая же проблема присутствует (лат. С и кир. C). Я эту проблему не пытаюсь решить.

> 2. Более высокий: набор символов. Один и тот же символ ("ъ") может использоваться в разных языках, может вне языка, может хоть в роли части орнамента. На этом уровне намеренно не затрагиваются такие понятия как слово, текст, язык. Unicode задает только набор символов.

Я поддержал бы в принципе такую идею, если бы юникод решил такое воплотить и ограничился уровнем 2. Но к сожалению, создатели пошли по другому пути и взяли что-то среднее между уровнем 2 и уровнем 3. Если бы они ограничились бы уровнем 2, то у нас бы не было кириллической "С" и латинской "C".

А раз уж они начали отличать латинскую C и кириллическую, то что нам мешает отличать немецкую и французскую?

> Слово "привет" — это еще Rust или уже русский язык?
очевидно же русский

> А это точно русский, может быть другой славянский язык?
это решает автор текста

> А кавычки — это русский язык или Rust?
не важно, их можно включить куда угодно, либо вообще вынести отдельно, вопрос обсуждаемый и решаемый. Вынести отдельно наверное проще, либо, как вариант, расположить их на одинаковом месте во всех подкодировках. Это второстепенный вопрос.

> А как терминал должен работать при выводе текстов на разных языках?

Текстовый терминал, который работает только в текстовом режиме? Как и сейчас. Ну, если у него есть соответствующий шрифт, то отображать текст, если нет - то нет, а что ещё он может делать?

> А как в текстовом редакторе отметить, что это русский язык?

Ну это вообще можно определить автоматически, при выборе раскладки клавиатуры, но если есть необходимость поменять язык, то можно предусмотреть и такую функцию, более того, в либреофисе уже есть такая функция - выделяешь текст и задаёшь язык. В чём проблема-то? По умолчанию язык берётся из настройки клавиатурного ввода.

> А если человек в мессенджере ввел "Привет", и мы получили это в нашей программе через API, то это на каком языке? А если он в следующим сообщением в этом же мессенджере отправил "добры дзень", то это на каком?

В чём проблема, я не понял. Мы через API получили строку, в которой задан язык, вот какой задан, такой и язык.

Имеется в виду, две строки с одинаковым начертанием? В юникоде ровно точно такая же проблема присутствует (лат. С и кир. C). Я эту проблему не пытаюсь решить.

Символы — это не начертание. Один символ может иметь разные начертания, и наоборот, какие-то начертания разных символов могут быть похожи.

Так же, как не стоит смешивать символы и текст, также не стоит смешивать и символы с их начертанием.

Конкретно здесь имеются в виду строки с одними и теми же символами, например:

  • строка "hotel" с английским языком и слово "hotel" с французским языком,

  • некая строка с указанным языком и та же строка без указания языка.

А раз уж они начали отличать латинскую C и кириллическую, то что нам мешает отличать немецкую и французскую?

Не смешивайте символ и его визуальное представление — часть вопросов отпадет.

В чём проблема-то?

  • Один написал "привет" с русской раскладкой, другой с беларусской, третий вставил из веб-страницы — и у вас три одинаковых по символам строки, которые при сравнении дают false.

  • Например, для Rust нет раскладки клавиатуры. То есть, вы неявно добавили к нашему вопросу еще и некое соответствие между языком и раскладкой клавиатуры, которое, вообще говоря, не один-к-одному, и не всегда имеется.

Так же, как не стоит смешивать символы и текст, также не стоит смешивать и символы с их начертанием.

Я согласен, лучше пользоваться принятой терминологией, чтобы понять друг друга. Я нашёл определение слова "символ" в викисловаре как "отдельный графический знак в письменности, например буква, цифра или знак препинания". Это вполне совпадает с моим пониманием, что символ это в основе своей "графический знак". То есть "смешать символы с начертанием" не получится, потому что символ представляет собой графический знак просто по определению. Это как бы и интуитивно должно быть понятно. Или я что-то упустил?

Или это намёк на то что по каким-то специальным лингвистическим правилам считается что символ "с" в русском и белорусском это один и тот же символ, а в английском и русском - два разных? Ну ок, я не лингвист. А как тогда правильно назвать то, о чём говорю я? Я хочу различать буквы русского алфавита и белорусского. Как тут правильно сказать? И я хочу общее название для латинского и кириллического знака "с".

> некая строка с указанным языком и та же строка без указания языка

Допустим у меня есть надпись на бумаге "сера". Это с одинаковым успехом может быть как латиница, так и кириллица или же их смесь. Причём разных комбинаций может быть 16. Это будут считаться надписи с одинаковыми символами или нет с точки зрения лингвистов?

> Один написал "привет" с русской раскладкой, другой с беларусской, третий вставил из веб-страницы — и у вас три одинаковых по символам строки, которые при сравнении дают false.

Прекрасно, вернёмся к юникоду, один написал "сера" латиницей, другой кириллицей, а третий половину так, половину иначе. В итоге у нас три одинаковых слова при сравнении дают false.

Юникод эту проблему никак не решает. Для того чтобы решить эту проблему надо кодировать одинаково символы с одинаковым начертанием - омоглифы по-моему называется.

Это в принципе неплохая идея, потому что важно во многих областях, где может быть подделка имени. Например подделка имени домена или имени почтового ящика или никнейма на хабре.

Но можно это решить и другими способами (менее удобно и более трудоёмко), составив таблицу омоглифов и нормализуя строки для сравнения согласно этой таблице. Короче, эта проблема существует в любом случае и её надо решать и она достаточно успешно решается, но это не может быть аргументом за или против в данном обсуждении, потому что существует точно также в юникоде.

> Например, для Rust нет раскладки клавиатуры.

Зачем для Rust раскладка клавиатуры? Ключевые слова в нём явно английские, то есть английского хватит. Если разрешены имена переменных или функций с другими символами, то можно опять же использовать буквы других языков, если нет, то опять же английского хватит. Зачем усложнять? Но если ооочень захочется, то можно конечно же сделать и специальную подкодировку для раста и раскладку, не вижу технических препятствий.

Я бы очень не хотел, чтобы помимо латинской «с» и кирилитической, у нас были бы еще и французская, вьетнамская и суахили. Это же одна и та же буква по сути. Но вот тегов языка очень не хватает, да.

Я бы очень не хотел, чтобы помимо латинской «с» и кирилитической, у нас были бы еще и французская, вьетнамская и суахили.

Ну тогда надо убрать "латинскую" "c" и "кириллическую" "c" и оставить просто "c".

И да, возможность задать язык очень нужна.

Мне кажется, что не получится ввести рабочее определение текста языка. На каком языке написаны тексты:

  • Программа на python с комментариями на русском

  • А научпоп, где наряду с русским текстом используются греческие буквы и символы шахматных фигур

  • К какому языку отнесена запятая между \alpha x, ы?

Программа на python с комментариями на русском

python это подмножество английского, не уверен что ему нужная своя подкодировка, можно обойтись и английской.

Очевидно же, что файл может содержать текст на разных языках, в частности на русском и английском. Можно хоть каждый символ делать на своём языке. В принципе в любом месте может понадобиться переключить язык.

> А научпоп, где наряду с русским текстом используются греческие буквы и символы шахматных фигур

Ну так греческие буквы или слова будут вставками греческого, а символы шахматных фигур - вставками "шахматного". Для каких-то математических символов тоже понадобится своя подкодировка математического языка. В сущности, разница с юникодом лишь в способе кодирования. Моя идея - увязать этот способ и с языком заодно.

Моя идея с языками не относится непосредственно к способу кодирования. На самом деле, можно и сам юникод расширить французским, немецким, итальянским алфавитом, он это позволяет. Просто вместо латинской С и кириллической С будет ещё французская С, немецкая С и т.п. Но это довольно расточительно, так как различных языков более 7 тысяч (а есть ещё спец. языки), а задать режим кодировки один раз в начале строки не так расточительно, на мой взгляд.

> К какому языку отнесена запятая между \alpha x, ы?

Со знаками препинания (и пробельными символами) можно поступить по-разному. Либо вынести их в отдельную группу, либо как-то иначе их сделать одинаковыми (например равными по порядковому номеру) во всех языках. Тут не уверен как лучше. Если выносить в отдельную группу, то знаки препинания будут занимать 4+ байт. В принципе терпимо, так как они редко встречаются.

Да, для пользователя JavaScript или Python проблема может быть неочевидна, т.к. в этих языках есть только тип string

Ну не правда, в питоне есть bytes. Встроенный тип, уже много лет. Наверное, с момента распространения юникода. На строки похож, но внутри - байты. Очень полезная иногда вещь.

На строки похож, но внутри - байты.

Настолько похож, что в python2 он назывался string :)

Ну, это было в старые добрые времена, когда ещё не было всяких енитих ваших юникодов))

Не вижу как можно перекодировку с этого в UTF-16 и UTF-8 векторизовать. Без векторизации все эти ужимки и приседания будут медленее текущих решений по перекодированию.

А текущие решения по перекодированию кодировок переменного размера каким таким волшебным образом векторизуются?

Давно уже в стандартных библиотеках языков. Статьи про ускорение пишутся уже не первое десятилетие. https://woboq.com/blog/utf-8-processing-using-simd.html на первой странице Гугла.

Какая-то чёрная магия, которая тут тоже применима.

Не очень, т.к. очень много зависимостей по данным (из-за переключения режимов)

Четырёх байтовые: UCS-4 фиксированной ширины и UTF-32 — переменной

Это одно и то же.

UTF-8 довольно простой, если не считать приколов с суррогатными парами

В UTF-8 нет ниаких "приколов" с суррогатными парами, суррогатные пары -- исключительная вотчина UTF-16

Но большая часть из них до сих пор не используется

Сегодня не используется, а завтра используется.

решать вопрос битых данных надо не на уровне кодирования текста, а на транспортном уровне

Вот есть у вас бинарный файл неизвестного формата, требуется найти в нём текстовые куски, как вы решите тут "вопрос битых данных"? Или есть битый диск со слетевшей файловой системой, как вытащить с него все возможные текстовые данные?

проблема компактного представления остаётся

Нет такой проблемы, вы её выдумали только ради того чтобы герически решить.

Это одно и то же.

https://www.ibm.com/docs/en/i/7.6.0?topic=unicode-ucs-2-its-relationship-utf-16

В UTF-8 нет ниаких "приколов" с суррогатными парами, суррогатные пары -- исключительная вотчина UTF-16

https://en.wikipedia.org/wiki/UTF-8#Surrogates

Вот есть у вас бинарный файл неизвестного формата, требуется найти в нём текстовые куски,... Или есть битый диск со слетевшей файловой системой, как вытащить с него все возможные текстовые данные?

Нет такой проблемы, вы её выдумали.

Сслыка про UCS-2 и UTF-16. Я говорил про UCS-4 и UTF-32.

То, что нет кодепойнтов от U+D800 до U+DFFF -- это не "приколы с суррогатными парами", это просто факт, что таких кодепойнтов нет. Впрочем, по крайней мере понятно, о чём, возможно, вы хотели сказать.

Нет такой проблемы, вы её выдумали.

Есть. Как и ещё миллион разных ситуаций, где текст обрубается в произвольном месте между байтами. Даже банально из-за программных ошибок когда неверно индекс подсчитан -- я сам такие делал и хорошо, что Rust проверяет такие вещи и проверяет именно с помощью свойств UTF-8.

Подумайте также вот над чем. 1. UTF-8 придумал Кен Томпсон, при этом Кен Томпсон понимал что делает и зачем. 2. Кен Томпсон сделал код самосинхронизирующимся. 3. Из 1 следует: если бы это не было необходимо, Кен Томпсон не стал бы делать код самосинхронизирующимся. 4. Из 3 и 2 следует: самосинхронизация необходима. Таким образом, строго логически можно доказать, что самосинхронизация необходима.

Ваше доказательство требует, чтобы Кен Томпсон был безошибочен сейчас и в будущем. Это неплохо бы доказать.

А, ну да, UCS-4 в какой-то момент "переопределили". Славно, что мы выяснили этот очень важный вопрос.

Да-да, я не понимаю, это другое.

Читаю и не понимаю, как в итоге это всё должно работать. Вот две цитаты из текста:

Одним байтом переключились на нужную страницу и далее в рамках этой страницы каждый байт со значением до 128 — один символ

Кстати, да, французам с диактрикой и украинцам с их Ґ мы помочь не сможем — придётся им переключаться между страницами, что в худшем случае выливается в 3 байта на одиноко стоящий такой символ вместо 2 у UTF-8

Вопрос 1: Почему, например, тот же Ґ нельзя добавить в блок "украинский" (да и во французском не так много диакритики)? 128 символов для алфавита выглядит вполне достаточно, чтобы нужные символы впихнуть. Или принципиальная позиция: что сейчас с диакритикой, то тут будет кодироваться 3 байтами? Так для украинского эта буква цельная. Иначе мы можем договориться, что в русском блоке не будет "ё" и "й", потому что это на самом деле "е" и "и" с диакритикой. Как вообще определяется что идёт в основной блок, а что в расширенный?

Вопрос 2: мне так кажется, что в большинстве языков с латиницей диакритика есть, в некоторых её даже очень много. Значит, для этих языков размер итогового "текста" вырастет по сравнению с utf8? Например финский или шведский с их ö/ä и ø.

Читаю и не понимаю, как в итоге это всё должно работать

Если точнее, то я не понимаю, как всё будет биться на блоки.

  1. Проблема в том, что в Юникоде символы таких языков, как французский и украинский, находятся в нескольких разных блоках, от чего приходится тратить байты на переключение между ними.

  2. Да, но не сильно: +1 байт за каждую одинокую диактрику.

  3. Блок в данном случае - это просто 128 последовательных пода и всё.

Понял, спасибо за разъяснение.

Ну чтош, осталось дело за малым, пропихнуть этот формат в качестве стандарта и убедить микрософт, гугл, мозиллу и эпл внедрить его в браузеры и ОС.

А в чём я не прав, отпишитесь хоть, кто минусы поставил?

НЛО прилетело и опубликовало эту надпись здесь

Потому что вы выдали унылую сентенцию в духе "ты никто и звать тебя никак".

Хоть кто-то мыслит нестандартно.

Может конечно и часть идей ТС откинется как нежизнеспособная.

Но массовке не оценить =)

Особенно, если её поддразнивать, так и будут минуса.

Автор просто выдумывает велосипед без внятных причин это делать. По этому ему справедливо и выдают минусов.

Я уж не говорю про сразу ряд неочевидных проблем, которые описали люди в комментариях.

По идее раз 23 "быстрых" символа взяты из ASCII, то для ASCII-блока не лишним был бы свой отдельный набор быстрых символов, а то он самым слабым выходит

Логично, а какие символы были бы наиболее полезны в дополнении к латинице?

Думаю, что тут всего вернее пробежаться по разным однобайтовым кодировкам и кастомным клавиатурам, что чаще встречается — то и востребованнее. Неразрывный пробел, евро, градус, параграф, несколько видов копирайта и тому подобное

Как минимум туда стоит поместить распространенную диакритику, что позволит записывать европейские языки в столько же байт что и utf8. Прада это потребует соотвествующей нормализации.

Если речь про цельные символы вроде Ä, то сколько-либо адекватный их набор туда вряд ли влезет (в сообщении выше я еще забыл написать очевидное про тире и кавычки разных видов, одни они уже наверняка отожрут треть или четверть места), а вот комбинируемая диакритика может и была бы неплоха для новых текстов, но думаю, что цельные знаки в существующих текстах и клавиатурах (да и в софте, телеграм вон комбинируемую по сей день не тянет) сильно популярнее сборок, так что при взаимно однозначной конвертации экономия выйдет спорная. По уму еще можно на большом массиве латинописьменных текстов пройтись бы простейшим скриптом на статистику по символам ну или положиться на ответы ллмок, раз уж предсказание символов по огромной выборке как раз лежит в основе их действия

UDP, один пакет потерялся...

о они могут приходить в разном порядке?А знать ли вы, чт

Нене, это пофиг, порядок известен и восстанавливаем, вместе с указанием на режим. Если пакет с указанием не потерялся...

Потерянные пакеты тоже не сложно запросить повторно.

Кодировка UTF-8 обладает одним важным достоинством: алгоритм парсинга имени файла (полного, с путём) в ней ничем не отличается от ASCII. Т.е., алгоритм для ASCII будет работать и с файловыми путями в UTF-8, не замечая разницы.

Я ведь правильно понимаю, что UCF этим свойством не обладает?

Сомнительное свойство, конечно, но обладает. А вот utf16 - нет, и никто не страдает без него.

Сомнительное?

Это одна из основных причин, почему UTF-8 относительно шустро стал стандартом де-факто. В большом количестве случаев (большинстве, кроме непосредственно вывода) алгоритму наплевать на конкретные языки и вся обработка сводится к манипуляции обезличенными наборами байт (слов, двойных слов).

UTF позволяет использовать одно и то же представление для хранения на диске и в памяти, для передачи по сети и между компонентами. Это очень удобно.

Сомнительное, ибо такие хаки приводят к уязвимостям.

Никакой это не хак, вот вообще. Вы либо меня не поняли, либо сознательно передергиваете.

В качестве элементарного иллюстративного примера можно привести разбор JSON с его произвольным UTF-8 в строках. Парсеру не надо вообще ничего знать про кодировки, ему что ASCII, что UTF-8, все едино -- достаточно искать разные скобки и кавычки.

То есть строку в UTF-8 можно прочитать из файла или принять из сети и без преобразования распарсить и передать фрагменты дальше.

Вы в UCF упомянули небольшой набор "базовой ASCII пунктуации", но разница тут фундаментальная. Потому что дело не только в конкретном парсинге от скобки до скобки.

Над UTF строкой можно выполнять различные операции с привычной алгоритмической сложностью. В случае UCF это невозможно и надо сначала преобразовать строку к какому-нибудь вменяемому виду -- например UTF или UCS.

Вы не знаете JSON: https://habr.com/ru/companies/vk/articles/314014/

Вот именно алгоритмическая сложность любых операций что с UCF, что с UTF-8 одинаковая. Кроме одного маргинального исключения - тыкнуть в произвольный байт в середине текста и начать декодировать оттуда символы.

французам с диактрикой

Простите, с чем?

Забавно, всю жизнь читал это слово не правильно.

аналогично, походу

Вы же, надеюсь, помните, что в строковых массивах не должно быть нулевых байтов? API ядра всё ещё сишное...

Сишные псевдостроки, очевидно, не подходят для хранения UCF как и любых кодировок с режимами. А апи ядра нужно скармливать строки в его кодировке, которая у разных ядер разная.

Апи ядра надо скармливать строки в той кодировке, в которой вы хотите видеть логи и названия файлов.

Вот от такой безответственности и появляются крякозябры тут и там.

Z̸̢̀ͅá̴̧̗͚l̸͈̖̆̒͝g̴̲̈́̅o̸̡͗̾̈͜͜ ̷̼͓͓̏͑͝н̵͓̦̲̆е̸̡͈̒̚͘д̸̧͎̏͒͘о̵̫̰͋ͅв̷̘̦̬̿̔͛о̷̯̫̑̿̋л̸̯̘͈̊е̴̱͕̭̂͂̄н̸̭̌̍

А чего не доволен-то? На 30% меньше получается.

Каждый пробел в русском тексте будет кушать 3 байта.

А в UTF8 как? Там два байта для кодирования всего 2048 (-128) символов, вместо 16384 у leb128.

Кодировки с переменной длиной - дичь, сколько там всего долей процента текстовой информации вообще по сравнению с каким-нибудь видео, чтобы не сношать уже мозг и какой-нибудь фиксированный utf32 использовать. А лишние нулевые байты любая примитивная компрессия поверх и так уберёт.

В utf8 пробел 1 байт, но каждая буква - 2.

как тогда тот же код пробела 32, закодированный в leb128, станет вдруг 3 байта???

А, подумал вы про дифференциальное кодирование. Кириллица в uleb128 будет как и utf-8 - двухбайтовая.

Четырёх байтовые: UCS-4 фиксированной ширины

Эта "фиксированная ширина" тоже достаточно условна и работает пока мы не используем диакритику.

там место под коды в уникоде закончилось что ли, чтобы все эти точечки, кружочки, чёрточки, домики и всевозможные их комбинации вместе с буковками закодировать отдельно? не настолько же их много.

Тут речь о том, что некоторые символы представляются несколькими кодепоинтами, так что символ "🏴‍☠" в UCS-4 занимает 12 байт, так как состоит из двух эмодзи, соединённых специальным соединителем. Каждый по 4 байта.

Кажется, вы изобрели японскую ISO-2022-JP, со всеми её минусами. Из-за переключения режима кодирования эта кодировка сложна при обработке строк и создаёт риски XSS, настолько, что современные браузеры её или не автодетектят или вообще не поддерживают.

Её «младшая сестра» Shift-JIS проще, но даже её уже почти полностью вытеснила UTF-8.

Когда в следующий раз захотите ляпнуть какую-нибудь глупость - отключитесь от интернета, чтобы она не осталась в нём навсегда.

Самокритичность - очень ценная черта характера, уважаю!

Такая кодировка не позволяет делать zerocopy парсинг. Плюс, она не позволяет брать подстроки (что часто используется в дедупликации).

Вообще кодировка скорее похожа на доменноспецифичный алгоритм сжатия, чем на собственно кодировку. Но в таком случае, кажется, можно сделать сильно эффективнее(быстрее, компактнее), если подходить к этому именно как к сжатия (и можно жать не байты, а напрямую кодпоинты).

P.S. кажется Вы это подразумевали, но было бы хорошо добавить в статью чёткое указание, что это кодировка для передачи, а не для использования в оперативной памяти.

Все она позволяет. В спеке даже приведены рекомендации, как это реализовать.

Каким образом может быть zerocopy парсинг, если нам в памяти нужен utf-8/utf-16? Или Вы о чём конкретно говорите?

Странный аргумент. Так никакая кодировка не позволяет zerocopy парсинг, если в памяти нужна другая :)

Ну, это как бы логично. Но создаётся впечатление, что онтоп кодировка не предназначена для использования в памяти. Поэтому и аргумент

не нашёл, почему такое впечатление?

Потому что при работе с ней в памяти возникает много сложностей. Да и вроде автор в комментариях не говорит о внедрении как стандарт.

Мне кажется, проблема компактности текста мало где актуальна. Тексты в целом весят мало. С юникодом дофига других проблем (начиная с того, что есть много юникодов). Есть даже уязвимости, когда по разным collation можно взламывать сайты. Что-то вроде регистрации юзера ádmin (первый символ - не обычныая "a"), но иногда при логине или восстановлении пароля эта запись может находиться и обрабатываться первой.

Я бы был очень рад если бы появилось решение этой проблемы, и у нас была бы единая простая кодировка (пофиг, пусть объемная) с которой было бы просто и понятно работать, почти как с ASCII.

Забавно, если скопировать URL из браузера в буфер обмена, получаем %D0%90%D0%90, как будто живы еще системы не работающие свыше 7 бит, а другие вещи мы депрекатим.

Firefox:

browser.urlbar.decodeURLsOnCopy (boolean, default: false)

Whether copying the entire URL from the location bar will put a human readable (percent-decoded) URL on the clipboard.

https://ru.wikipedia.org/wiki/Троичный_компьютер

PS: там еще веселости есть, после ввода в адресной строке показывается следующей строкой

  • browser.urlbar.suggest.calculator=true -> 32+16 // => 48

  • browser.urlbar.unitConversion.enabled=true -> 12in to cm // => 30.4800000001 cm

Недавно переписывал reverse iterator UTF-8 строки поверх std::string. Отматываешь назад, пока старшие биты 0b10xxxxxx до тех пор, пока не встретишь 0b110xxxxx. А тут что, mission impossible?

Думаю да. Если у вас нет каких-то светлых идей.

ВЕРДИКТ:

Статья — отличный пример инженерного онанизма.
Технически — он решил задачу "как упаковать плотнее".
Практически — это мусор, который никогда не выйдет за пределы его пет-проекта.

  • UTF-8 победил не потому, что он самый компактный.

  • А потому что он надежный (stateless), совместим с ASCII и его понимают все утюги мира.

Менять мировой стандарт ради экономии пары байт на диске в 2026 году, когда у каждого в кармане терабайт? Ну удачи, Дон Кихот.

Главная проблема: СОСТОЯНИЕ (Statefulness)

В этой кодировке невозможен Random Access (произвольный доступ), невозможен seek, невозможно восстановление после битого пакета (потерял один байт переключения — весь остальной текст превратился в мусор).

2. Сжатие vs GZIP

алгоритмы работают на уровне энтропии.

4. Безопасность (Security Nightmare)

Stateful-кодировки — это рай для хакеров.

  • Как фильтровать XSS (вредоносный скрипт)?

  • В UTF-8 ты ищешь байты <script>. Они всегда одни и те же.

  • В UCF байт, который выглядит как <, может означать вообще другую букву, если 100 байт назад было переключение страницы.

  • WAF (Web Application Firewall) и базы данных охереют это проверять. Это дыра в безопасности размером с тоннель.

PS: самому было лень все это писать, автор займись чем-нибудь полезным, а это полный кринж

  • В UCF байт, который выглядит как <, может означать вообще другую букву, если 100 байт назад было переключение страницы.

    это stateful кодировка, при чтении строки нужно помнить страницу и проблем нет, аналогично в UTF-8 при чтении с середины буквы

    сразу напишу тут про вставку и подстроки: для подстрок либо откат назад для выяснения страницы, либо извлечение страницы из контекста; для вставки — просто дублирование страницы; для сравнения — нужна нормализация, но она и в юникоде нужна, при этом нормализация для страниц элементарна и сравнивать можно по хэшу (вероятностное сравнение, в реальности использовать солёные хэши), либо принять, что строки со вставками и без — разные

В UTF-8 можно тыкнуть в середину текста, смещаясь и проверяя код найти начальный байт и получить символ. У вас символ в середине будет зависеть от страницы, которая задана неизвестно где.

Это значит вам дадут фрагмент с килобайт посреди мегабайтного текста, а он из однобайтных символов, но неизвестно какой страницы.

Так что очень сомнительно. Разве что, повторять код страницы каждые n байт. Но это уже кривовато выглядит. А если на кириллические буквы вместе с постоянным указанием страницы так и будет уходить два байта то вообще нет улучшения.

То что пробел между словами уже не 0x20 это, конечно, отдельный прикол.

нормально это выглядит

Если мне дадут этот фрагмент в кодировке UCS, то у него будут все необходимые переключатели режимов как вначале, так и в конце. Если мне дадут фрагмент байт вырезанный в случайном месте, то ума не приложу, что мне делать с этим фрагментом, где крайние слова обрезаны по середине.

Сначала был 7-битный ASCII и все телетайпы понимали друг друга. И было это хорошо.

Подождите, сначала были 5-битные телетайпы, гуглите Baudot/ITA-2. Пять бит - это 32 индивидуальных значения, более чем достаточно для 26 букв, 10 цифр, и набора знаков препинания вместе со служебными символами вроде перевода строки. Ну, как достаточно - два из служебных символов только то и делали, что переключали регистр с условно букв на условно цифры плюс знаки препинания, ну и обратно, так что при помощи нехитрого хака из пяти бит получалось 64 значения.

А где живет официальная спецификация данного изобретения, если она существует вообще? Или эта статья и есть спецификация? Чтобы уже начать с ней работать с полным пониманием сабжа. На вашем сайте тот же объем информации, что и в статье.

А какой информации вам не хватило?

Например, перечень кодов всех страниц. Их около 100 вроде-бы, или нет

На диаграмме все коды видны.

Ссылку можно на диаграмму? Какой код у страницы с корейскими иероглифами, японскими, тайским алфавитом? Где это все? Может я тупой, что-то не догоняю?

Слово МИР имеет код 3С 38 40. Хорошо, а коды остальных русских букв где?

Если код русской страницы A4, то что означает код 08 сверху.

Порядковый номер.

Да, тяжело с вами общаться. С таким отношением вряд ли вы найдете сподвижников. И даже если кто-то заинтересуется, то скорее свой аналог придумает. Кстати -1 не я вам поставил.

Вот так вот отвечаешь на глупые вопросы, а тебе ещё и предъявы кидают.

Сильно усложнится поиск и редактирование строк. Допустим мы ищем в тексте строку с русскими и латинскими символами. Она начинается с байта переключения на страницу русских символов. А в тексте такой последовательности байтов нет, он в основном на русском, и код переключения на страницу русских символов находится в самом начале. Поэтому стандартные механизмы поиска не будут работать. Тут вообще не очень понятно, как сделать поиск.

Регулярные выражения тоже работать не будут. Я не уверен, что в таком подходе они вообще возможны, в любом месте текста может встретиться код переключения страницы, который не соответствует паттерну. В крайнем случае надо будет переписать все движки регулярных выражений.

В текстовом редакторе при изменении позиции курсора надо будет каждый раз проверять каждый байт текста от позиции курсора в обратном порядке и проверять где там предыдущий код переключения страницы, чтобы определить, добавлять его при вводе символа или нет. Аналогично со вставкой текста, потому что он начинается с кода переключения символа.

Вы сэкономили несколько байт на передаче текста один раз, которые на фоне размера картинок выглядят незначительно, зато увеличили время процессора для его обработки постоянно.

Тут вообще не очень понятно, как сделать поиск.

Почитать спеку, там всё есть.

В крайнем случае надо будет переписать все движки регулярных выражений.

Их уже переписали на матчинг по кодовым точкам, а не байтам.

В текстовом редакторе

Никто не будет двигать мегабайты туда-сюда на каждое нажатие клавиши в начале файла. Они работают не так, как вы наивно полагаете.

Аналогично со вставкой текста

Аналогично читаем спеку.

передаче текста один раз, которые на фоне размера картинок выглядят незначительно

Текст и картинки имеют разную критичность. Стыдно этого не знать.

Почитать спеку, там всё есть.

"Спекой" вы называете вот это коротенькое описание?
Почитал, ответа на этот вопрос там нет. Есть одна строчка с фантазией на тему что можно попробовать. Которая не будет работать.

text = 'Тут есть строка 1ab и AB@>:0 1ab'
// UCF:
// \xA4 " C B \x95 5 A B L \x95 A B @ > : 0 \x95 \x81 \x9C a b \x20 \xA4 8 \x95 \x9C A B @ > : 0 \x20 1 a b
//      Т у т   _  е с т ь   _  с т р о к а   _    1       a b   _       и   _       A B @ > : 0   _  1 a b
  
str = 'строка 1ab'
// UCF:
// \xA4 A B @ > : 0 \x95 \x81 \x9C a b
//      с т р о к а   _    1       a b

str = 'строка'
// UCF:
// \xA4 A B @ > : 0
//      с т р о к а

str = '1ab'
// UCF:
// 1 a b
// 1 a b

Занятно, что у вас пробелы и цифры кодируются по-разному в зависимости от предыдущего текста. Так становится еще более непонятно.

Ну давайте, почитайте вашу спеку и напишите тут код функции поиска, которая будет выдавать только позицию 9 для первого случая, только 9 для второго случая, и 16 и 29 для третьего. Раз вам все понятно, это вам не составит труда.

Никто не будет двигать мегабайты туда-сюда
Они работают не так, как вы наивно полагаете.

Я не говорил ни про какие движения мегабайтов туда-сюда. Читайте комментарий внимательно.

со вставкой текста
Аналогично читаем спеку.

Там написано то же самое, что я написал в комментарии.
И нет, "её последний режим кодирования" не будет работать, потому что в текстовом редакторе можно делать вставку в середину строки. Дальше подумайте, что вы будете делать с исходной "ненаивной" строкой и как будете использовать позицию курсора.

Их уже переписали на матчинг по кодовым точкам

И? От этого они магически будут не считать символами текста ваши переключатели страниц?

Текст и картинки имеют разную критичность. Стыдно этого не знать.

Стыдно подменять понятия. Критичность к разговору про размер данных не имеет никакого отношения. Вы заявили про компактность, но эффект в реальных приложениях незаметен. Не говоря уже о том, что обычный ZIP дает лучшую компактность.

Согласен, на уровне байтов есть недетерминированность представления чисел и базовой пунктуации. Матчиться надо на следующем уровне абстракции - по кодовым точкам. На этом уровне нет ни недетерминированности, ни переключателей страниц. Поправил спеку, спасибо.

В идеале было бы всегда кодировать их одинаково, но тогда сломается совместимость с ASCII, что довольно неприятно. Хотя, есть одна идейка..

Если вашу кодировку нельзя использовать как есть для работы со строками, и их нужно конвертировать во что-то еще, значит она нужна только для передачи данных. А для этого лучше использовать ZIP.

И нет, "её последний режим кодирования" не будет работать, потому что в текстовом редакторе можно делать вставку в середину строки. Дальше подумайте, что вы будете делать с исходной "ненаивной" строкой и как будете использовать позицию курсора.

В текстовых редакторах и так есть всякие структуры данных, которые позволяют считать всякие статистики на тексте (количество строк, количество софт-строк, номера колонок и прочее). Добавить к ним ещё одну статистику — не сложно (например номер текущей страницы). Поэтому конкретно в этом аспекте проблемы толком нет.

Какую конкретно статистику? У вас есть на экране абзац текста с русскими и латинскими символами, вы ставите курсор в середину строки. Вам надо пройти по этому тексту обратно и найти код переключения страницы. Если ставите курсор в другое место, надо еще раз сделать то же самое. Последний код переключения в строке тут не нужен.

Есть структуры данных, которые умеют быстро находить последний код переключения страницы в производьном диапазоне. И эти структуры данных в любом случае уже используются в редакторах.

Для примера, строп.

Edit: не везде используются стропы, разумеется. Там где не используются эти операции поиска могут быть не всегда быстрыми — однако в таких случаях операция "вставки в середину" тоже оказывается очень медленной. Просто потому что редакторам и так приходится отвечать на всякие запросы типа "найди последний перевод каретки до этой позиции курсора".

В качестве патологического примера, откройте в виме файл со строкой длинной в несколько МБ, включите wrap, и где-нибудь в середине этой строки введите несколько символов. На тех версиях, на которых я такие файлы редактировал у меня уходило по 3-30 секунд на нажатие.

Я не понимаю с чем конкретно вы спорите. Что это возможно сделать? Я не говорил, что это невозможно. Процитированный вами текст был про отсылку на "спеку" с фразой про последний режим кодирования строки, которая была использована как возражение на мое описание логики работы редактора при вставке. Я сказал, что это работать не будет, потому что нужен предыдущий режим в середине строки.

Я спорю с тем, что у редакторов могут возникнуть хоть какие-то сложности со вставкой в середину.

Или, если хотите, могу по-другому сформулировать: время поиска "предыдущего режима в середине строки" есть O-большое от времени собственно самой вставки в середину строки (например для случая utf-8).

Чисто потому, что, грубо говоря, редакторы никогда не занимаются вставкой "в середину строки". Там задачу переформатируют таким образом, чтобы этого никогда не нужно было делать.

Я не говорил, что у редакторов могут возникнуть сложности со вставкой в середину. Я сказал, что "последний режим кодирования строки" для этого бесполезен. Также я сказал, что это займет больше процессорного времени по сравнению со вставкой в середину строки c UTF-8.

Их уже переписали на матчинг по кодовым точкам, а не байтам.

Матчинг по байтам может быть кратно быстрее. Особенно для статичных фрагментов/поиска подстроки.

Но не учитывает разные формы представления одного и того же символа в юникоде.

Матчинг по кодпоинтам тоже не учитывает.

Пишите багрепорты, если где-то не учитывает.

Так он и не должен учитывать, т.к. разные представления одного и того же символа — это разные наборы кодпоинтов.

Ну, если вы кодепоинты ищите, а не символы, то конечно.

А что Вы тогда подразумеваете под словом "символ"? Я подразумевал "абстрактный символ" из юникода.

И раз уж Вы так уверенно говорите, можете привести примеры регексов, которые умеют обрабатывать разные представления символов?

In practice, regex APIs are not set up to match parts of characters or handle discontiguous selections.

Там же наоборот говорится, что регексы не умеют обрабатывать разные представления. Тем, кому это нужно там дана рекомендация "нормализуйте строку перед матчингом".

Applying the matching algorithm on a code point by code point basis, as usual.

И дальше подтверждается тезис, что регексы матчатся по кодпоинтам.

Эта спека не для конечных пользователей, а для разработчиков движков регулярок.

А кто это спеку реализует, какие движки регексов?

Предлагаю исследовать этот вопрос самостоятельно и написать про это статью.

Это Ваше высказывание было, что движки регексов учитывают нормализацию. Я с этим высказыванием как раз не согласен

Кстати, да, французам с диактрикой и украинцам с их Ґ мы помочь не сможем — придётся им переключаться между страницами, что в худшем случае выливается в 3 байта на одиноко стоящий такой символ вместо 2 у UTF-8

Интересно почему так для украинского, ведь и у украинского и русского по 33 символа в афавите.

Очередной пример попытки героического решения несуществующей проблемы. Причем решение сложнее и проблемные, чем сама проблема.

Как обычно, каждый Крюгер уверен, что вот он-то делает что-то важное, а все остальные какой-то дурью маятся, лишь бы не работать.

скажите... а кодирование туда-обратно... оно уже быстрее чем zip-ование "не-перекодированного 4х байтового" utf или ещё пока нет ? ;)

Да, а с чего бы ему быть медленнее?

Спасибо всем за конструктивную критику. Внёс несколько улучшений в формат, чтобы закрыть больше кейсов использования:

  • ASCII символы из "быстрого набора" теперь всегда кодируются одинаково независимо от режима, что даёт детерминизм представления символов, а следовательно и упрощает поиск по байтам.

  • Старшие байты вообще и преключатели режимов в частности теперь используют дифференциальное кодирование относительно последнего режима. Это позволяет итерироваться по строке в обе стороны. Так же пришлось в 2 раза уменьшить размер широких страниц (и соответственно удвоить их число), чтобы избежать неоднозначности при итерировании в обратном направлении.

  • Строка теперь не только начинается с ASCII режима, но и должна заканчиваться в нём. Это не только позволяет итерироваться с конца, но и делает конкатенацию совсем тривиальной задачей. Цена этому - возможное появление 1 доп байта в конце.

  • В ASCII режиме теперь доступна 21 штука однобайтовой диакритики без переключения режима. Для этого, конечно, нужен ещё правильный ввод текста или предварительная его NFD нормализация. Но это всё же лучше, чем ничего.

Код в итоге несколько усложнился и замедлился, что мне совсем не травится. Буду рад, если подкинете идеи, как его можно было бы упростить и ускорить.

Может хватит уже этой экономии на спичках? Давно нужен один стандарт, но не такой, а фиксированной длинны с уникальными кодами под все символы. Считать честно лень, но 4 байт вроде хватит чтобы впихнуть все + сразу заложить диакритику как отдельные символы и ещё запас останется. В рамках памяти это копейки, а по сети gzip везде по умолчанию. Uuid же начали как id в базах использовать и никто не ноет за лишние байты

Может хватит уже писать тупые комментарии? Читать лень, но даже Дипсик вроде смог бы написать что-то умнее. Вон выше чел уже вывалил свой нейрослоп, и никто не ноет, только лайкают.

Мне понравился ваш стиль изложения.

Что касается вашего технического решения: вставка управляющих символов смены режимов превращает данные в программу. И теперь, чтобы прочитать кусок 100гб лога придется парсить весь файл (в худшем случае).

Именно поэтому предложенный вами подход никогда не станет стандартом.

Логи лучше разбивать на чанки независимо от кодировки, а не писать 100гб файлы, и потом думать как бы его аккуратней пошринкать. Я уж молчу про построение индекса, чтобы искать в нем что-либо было не так дорого.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации