Исключительно быстрая валидация UTF-8

Автор оригинала: Daniel Lemire
  • Перевод
Текстовая строка — один из самых распространённых «типов данных» в программировании. Когда программисты думают о строке, то представляют список или массив символов. Это «достаточно хорошее» приближение, но реальность сложнее.

Символы должны быть каким-то образом закодированы в биты. Большинство строк в интернете, включая этот пост на Хабре, закодированы в UTF-8. Формат UTF-8 представляет «символы» в одном, двух, трёх или четырёх байтах. Это обобщение для стандарта ASCII, использующего только один байт на символ. То есть строка ASCII также является строкой UTF-8.

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

Есть и другие стандарты. Некоторые старые языки программирования, такие как C# и Java, полагаются на UTF-16. Там используется два или четыре байта на символ. В то время это казалось хорошей идеей, но сейчас консенсус движется к использованию UTF-8 всегда и везде.

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

Какое это имеет значение? Большое. Например, в веб-сервере Microsoft есть такая уязвимость: он принимает URI, который кажется действительным и безопасным, но после интерпретации сервером даёт злоумышленнику удалённый доступ к диску. Даже если оставить в стороне вопросы безопасности, вы почти наверняка не захотите сохранять недопустимые строки в своей БД.

Таким образом, языки программирования, веб-серверы, браузеры и движки БД постоянно осуществляют валидацию UTF-8.

Если ваши строки в основном просто ASCII, то проверки выполняются довольно быстро, и проверка UTF-8 не является проблемой. Но уже прошли те дни, когда большинство строк кодировалось в ASCII. Мы живём в мире эмодзи и множества национальных алфавитов.

Ещё в 2018 году я задался вопросом: насколько быстро можно валидировать строки UTF-8? В то время я нашёл вариант валидации в несколько циклов CPU на символ. На этом можно было успокоиться, но меня такой ответ не удовлетворил.

Работа заняла годы, но кажется, теперь мы получили вариант, близкий к идеальному. Новый алгоритм на порядок быстрее, чем другие варианты быстрого поиска. Мы подготовили научную статью: «Валидация UTF-8 менее чем за одну инструкцию на байт» (будет опубликована в журнале Software: Practice and Experience), а также опубликовали утилиту для бенчмаркинга.

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

Во всех процессорах есть быстрые инструкции SIMD. Они работают с широкими регистрами (128 бит, 256 бит и т. д.). В большинстве наборов имеется инструкция «векторизованного поиска», которая принимает, скажем, 16-байтовые значения (в диапазоне от 0 до 16) и ищет их в 16-байтовой таблице. В процессорах Intel и AMD этому описанию соответствует инструкция pshufb. Значение в диапазоне от 0 до 16 иногда называют nibble, оно охватывает 4 бита. Байт состоит из двух nibble (низкий и высокий).

В нашем алгоритме поиска векторизованная инструкция поиска вызывается три раза: один раз для низкого nibble, один раз для высокого и один раз для высокого nibble следующего байта. У нас три соответствующие 16-байтовые таблицы поиска. Если выбрать их правильно, то побитовое AND из трёх поисков находит любую ошибку.

Подробнее см. в научной статье, но в конечном итоге почти полная валидация UTF-8 выполняется всего пятью строками быстрого кода C++ без каких-либо ветвлений… и эти пять строк за один раз проверяют блоки до 32 байт…

simd8 classify(simd8 input, simd8 previous_input) {
  auto prev1 = input.prev<1>(previous_input);
  auto byte_1_high = prev1.shift_right <4>().lookup_16(table1);
  auto byte_1_low = (prev1 & 0x0F).lookup_16(table2);
  auto byte_2_high = input.shift_right <4>().lookup_16(table3); 
  return (byte_1_high & byte_1_low & byte_2_high);
}

Хотя это сразу не совсем очевидно, но такой валидации достаточно, и она безопасна на 100%. Это действительно так. Остаётся всего несколько недорогих дополнительных технических шагов.

В результате на последних процессорах Intel/AMD требуется чуть меньше одной инструкции на байт для проверки даже самых мусорных, случайных входных данных. Поскольку код исключительно оптимизированный, вы можете выйти на три инструкции за цикл, и даже больше. То есть мы используем небольшую часть цикла (менее трети) на входной байт на современном CPU. Таким образом, скорость обработки надёжно поддерживается на уровне более 12 ГБ/с.

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

Если вам необходимо использовать функцию быстрой валидации UTF-8 в продакшне, рекомендуем библиотеку simdjson (версия 0.5 или выше). Она хорошо протестирована и имеет полезные встроенные функции, такие как диспетчеризация во время выполнения. Хотя библиотека создана для парсинга JSON, вы можете использовать её чисто для валидации UTF-8, даже если никакого JSON нет. Она поддерживает 64-битные процессоры ARM и x64, а также имеет резервный вариант обработки для других CPU. Мы упаковали её в один заголовочный файл вместе с одним исходным файлом; так что можете просто поместить её в свой проект C++.

Предыдущие работы. Основная заслуга в популяризации метода векторизованной классификации, который является ключом к алгоритму поиска, принадлежит Муле. Насколько мне известно, Кейзер впервые предложил нашу стратегию тройного поиска. Первый практический алгоритм валидации UTF-8 на основе SIMD создал К. Виллетс. Несколько человек, включая З. Вегнера, произвели улучшения в нём. Трэвис Даунс предложил грамотные идеи, как ускорить обычные алгоритмы.

Дальнейшее чтение. Если вам нравится эта работа, то могут понравиться другие статьи на смежные темы: «Кодирование и декодирование Base64 почти со скоростью копирования в памяти» (Software: Practice and Experience, 50 (2), 2020) и «Парсинг гигабайт JSON в секунду» (VLDB Journal, 28 (6), 2019).

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

    0
    Если проверяются блоки по 32 байта за раз — как решается проблема чтения не кратных 32-м байтам строк? Как обычно в подобных алгоритмах (по одному до выравнивания на 32, потом по 32, потом по одному до конца)? Не увидел в коде в документе.

    Если этого нет — то это попытка чтения за пределами строки.
      0
      Наверное алгоритм это только часть программы и так как они сами подготавливают для него строки для проверки, то просто знают где остановиться — на границе 32байт, а хвост добить до 32 байт нулями.
        0
        Не, как PoC годится, просто мне непонятно, неужели эту задачу нельзя решить с 512bit интринсиками? Там же чистейший матан, для чего они и создавались.
          0
          Не понял. Они эту задачу решают in general, без привязке к ширине инструкций — какие есть самые большие в процессоре, такие и используют (кроме самых широких кажется, да, но они ещё мало где есть).
            0
            Я про MMX/SSE4 инструкции типа
             __m512   _mm512_add_ps  (__m512 a, __m512 b);                                        // calculates a + b for two vectors of 16 floats
             __m512   _mm512_fmadd_ps(__m512 a, __m512 b, __m512 c);                              // calculates a*b + c for three vectors of 16 floats

            Задача проверки битовых окон прекрасно подходит под интринсики, а подавляющее большинство текущих активных процессоров давно их поддерживает. Если их нет на данной архитектуре — это либо очень старый камень, либо какая-то экзотическая платформа.
              0
              AVX-512 есть далеко не везде, а точнее — вообще мало где. А всё остальное они и так поддерживают.
        +2

        хвост перекладывается в буфер и проверяется за 1 раз
        https://github.com/simdjson/simdjson/blob/master/src/generic/stage1/utf8_validator.h#L18

          0
          А тексте терминологический ад: «16-байтовым значением» тут называют 4-битное значение, а число 16 значит количество возможных уникальных значений
          +2
          Взяли бы кодировку в 4 байта на символ и все эти проблемы с парсингом/дырками просто не появились. А так получилось «мы хотели сэкономить пару байт, а получилось как всегда». При текущем развитии хранилищ данных — больше актуальная проблема с обработкой (распарсить этот текст) чем с недостатком места.
            +2
            Вообще, процессоры сейчас всё больше и больше упираются в memory wall — неспособность памяти поставлять данные достаточно быстро к вычислительным ядрам, а вы предлагаете ещё в два-четыре раза раздуть строки, чтобы что? Чтобы не придумывать алгоритм из пяти строк, который валидирует быстрее, чем работает memcpy?
              +1
              Сколько тактов и обращений к памяти потребуется процессору чтобы разобрать на составляющие эту utf-8 строку, при неравномерности символов?

              Да и вопрос с memory wall и текстом достаточно некорректен — именно текста там практически нет. У вас одни формы для отрисовки занимают несравнимо больше данных.
                +1
                Сколько тактов и обращений к памяти потребуется процессору чтобы разобрать на составляющие эту utf-8 строку, при неравномерности символов?

                Издеваетесь? Ваш комментарий прям под научной статьёй про валидацию таких строк и скорость там = скорости памяти. Или я не понимаю, что значит «разобрать на составляющие», посмотрите как те же авторы JSON парсят на скорости 2.5гбайта/сек на одном ядре и utf8 там далеко не главный боттлнек.

                Memory wall прекрасно memory wall для всех нагрузок — если вы не работаете с текстом на больших объёмах — это не значит, что никто не работает. «Формы для отрисовки» однозначно выдают то, что вы подумали про вычислительные нагрузки в виде какого-нибудь браузера у себя на макбуке, а не про датацентр из миллиона серверов, который обеспечивает вам лёгкий поиск в гугле например.
                  0
                  Издеваетесь? Ваш комментарий прям под научной статьёй про валидацию таких строк

                  Статья про валидацию (то что строчка пришла действительно utf8), а не про разбор строки для дальнейшей работы и дальнейшей работы с этой строчкой.

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

                  При входном потоке который не требует валидации что это utf8 и дальнейшей перекодировки для работы — большая часть проблем для вас исчезает. И оборудованию становится сильно проще.
              +1
              В большинстве *nux тип wchar_t как раз 32-битовый. Вопрос не в хранении а в совместимости при передаче данных в «текстовых» протоколах Интернета.
                0
                Все можно сделать со временем, просто это займет много времени. http2 запилили — можно было уже начинать менять и другие стандарты, а старые костыли готовиться выкидать. В качестве смены костылей — ipv6 потихоньку внедряется по миру — и там уже адресацию взяли с солидным запасом.
                  0
                  В большинстве юникс-подобных систем wchar_t практически не используется. Везде UTF-8.
                    0

                    Не используется он в GTK и компания, а нормальные системы используют штатные локали со стандартными mbtowc/mbstowcs и преобразуют любые локали в wchar_t для работы, а не только UTF-8.


                    И снова приведу в пример японцев: у них до сих пор Unicode практически не используется — у них несколько JIS локалей, старого доброго доюникодного ISO 646.

                      0
                      Например? Я не могу вспомнить ни одной программы, которая использует внутри wchar_t в мире Linux, и кучу примеров с UTF-8.

                      Есть Qt с QString с UTF-16, но это вообще ни туда, ни сюда.
                        0

                        Дак вы сами можете множество из них найти. Например, GNU gettext:


                        gettext-0.18$ grep -nr wchar_t | wc -l
                        1298

                        — В общем-то, практически все, кто локализует сообщения в *nix, используют ту или иную реализацию gettext.


                        Если мы говорим об Unicode, то наметилась тенденция использования libunistring для его поддержки:


                        libunistring-0.9.10-4-r2$ grep -nr wchar_t | wc -l
                        1065

                        Ну и далее:


                        • libxml2 — 18;
                        • libxslt — 6;
                        • libidn2 — 190;
                        • GLIB — 261;
                        • ncurses — 914;
                        • openldap — 156;
                        • perl — 34 (это очень старый perl);
                        • pam — 8.

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

                          0
                          Не, что в библиотеках есть wchar_t-совместимые интерфейсы — это хорошо. Но используются ли они в конечных программах?

                          Да и я вот посмотрел немного gettext, и там wchar_t как будто для Windows. Я не нашел ни одного вхождения wchar_t в хедерах, которые установлены gettext в моем линуксе.

                          В libxml2 тоже не нашел wchar_t в публичном интерфейсе.

                          ncurses действительно поддерживает параллельно char и wchar_t интерфейсы, это правда. Посмтрел несколько программ, которые зависят от ncurses — ncdu, htop — они используют char

                          > Если мы говорим об Unicode, то наметилась тенденция использования libunistring для его поддержки.

                          В документации libunistring есть такой занятный раздел: www.gnu.org/software/libunistring/manual/libunistring.html#The-wchar_005ft-mess

                          Я тоже накидаю примеров:

                          * CPython — wchar_t иногда используется для внутреннего представления юникодных строк. www.python.org/dev/peps/pep-0393
                          * Go — строки содержает произвольные байты, чаще всего UTF-8, изредка бинарные данные. blog.golang.org/strings. Выделенного юникодного типа нет.
                          * Rust — строки UTF-8. doc.rust-lang.org/std/primitive.str.html
                          * Linux (ядро) — все системные вызовы, принимающие строки, принимают нуль-терминированные строки char*, обычно семибитный ASCII или UTF-8 (хотя ядро никогда не знает, что это UTF-8, и никак с юникодом не работает (или очень редко)).
                          * libcurl — char* во всех интерфейсах.
                  +1

                  А как быть с нулевыми байтами? Одна из целей utf8 — поддержка сишных 0-терминированных строк. К тому же, когда его придумывали, юникод умещался в 2 байта на кодпойнт.

                    +1
                    Единственная цель появления utf8 — это сделать такой юникод, чтобы он остался совместимым с однобайтовыми текстовыми строками. Сегодня эта цель уже во многом забыта, потому что почти везде допускаются (и используются) любые символы.

                    Есть такая же дилемма, как RISC/CISC у процессоров. Постоянная ширина символа часто избыточна, но ей не нужен парсинг. Переменная длина символа экономит пару байт, но сильно усложняет обработку. Появляются некорректные последовательности (сабж как раз о них). А раз есть некорректные последовательности, значит есть и избыточность. Длину строки без полного парсинга определить невозможно. Адресовать символы без полного парсинга опять же невозможно.

                    Любая работа со строками в ЯП теперь непременно сопряжена с обращением к менеджеру памяти, про статические буферы/переменные можно забыть.

                    Я тоже считаю, что полноценный UTF-32 был бы удобнее почти во всем.
                      +1

                      Utf-32 тоже не панацея, т.к. потенциально надо работать с символами, состоящими из нескольких кодпойнтов. Так что учитывая, что в подавляющем количестве языков сейчас есть итераторы, utf-8 для внутреннего представления может оказаться вполне нормальным вариантом. В т.ч. в rodata. Любые строки (в т.ч. 8-битные) — это нетривиальность работы с памятью.

                        0
                        надо работать с символами, состоящими из нескольких кодпойнтов

                        Символ по-хорошему должен всегда умещаться в один «атом» строки. Если это не так, то смысла в длинном кодировании просто нет, и тут utf8 достаточно.
                          +1

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

                            +1
                            Например, буква Ё может быть представлена как комбинация двух кодпоинтов — Е и комбинирующего диерезиса

                            Такая ситуация называется «бардак» и говорит как раз о недостатках подхода. Как по-вашему должен работать поиск в таких условиях?
                              0
                              Как по-вашему должен работать поиск в таких условиях?

                              Поиск как раз хорошо будет работать, так как "Е" и "Ё" всегда будут просто "Е". А всякие точки и черточки можно и проигнорировать.

                                0
                                так как «Е» и «Ё» всегда будут просто «Е»

                                Так нифига же. Повсеместно встречаются оба варианта: единая буква, и Е отдельными точками.

                                А всякие точки и черточки можно и проигнорировать

                                Чтобы что-то игнорировать, нужно знать, что это такое и насколько оно (не)важно для целостности слова. Вот навскидку скажите, можно ли во французском языке игнорировать диакритику у символов "é", "è" и "ê" по аналогии с русской «ё»?
                                  0

                                  Что-то я не понял вашу позицию. Ведь сами русские пишут одно и то же слово, то через ё, то через "е". Например "ёлка" и "елка" это одно и то же.


                                  И конечно, если в поиске я напишу "елка", ожидаю что "ёлка" тоже будет найдено. И наоборот.


                                  ПП: И кстати, я не русский, я болгар. И мне было бы очень удобно, если "Э" и "Е" тоже были бы эквивалентны в поиске. Ведь в болгарском есть по сути только "Э" и иногда, я не знаю э или е нужно писать в некие слова.

                                +1
                                Такая ситуация называется «бардак»

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


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


                                Да, Юникод кажется переусложнённым иногда, но это отражение сложности систем письменности.

                              +1
                              Символ по-хорошему должен всегда умещаться в один «атом» строки. Если это не так, то смысла в длинном кодировании просто нет, и тут utf8 достаточно.

                              Перефразирую ваше утверждение: слово должно всегда умещаться в один «атом» строки. Если это не так, то смысла в длинном кодировании просто нет.


                              (Японцы, кстати, до сих пор не перешли на UTF, у них всё ещё главенствует JIS в нескольких кодировках.)


                              Ваше утверждение сродни утверждению «нет языка кроме английского».


                              Если мы даже возьмём русский без диакритических знаков (ё и й — самостоятельные буквы в русском), то есть знак ударения, который необходим для однозначного прочтения слова, и вы предлагаете ввести в UTF кодовые точки для всех гласных русского языка, которые могут быть ударными?


                              А что с арабским? А с хинди?


                              А дизайнерам шрифта вы предлагаете явно описать все символы вместо автоматической композиции сложных символов из ряда простых?


                              В UTF, кстати, сначала пытались впихнуть все символы, но столкнулись с реальностью и добавили композицию.

                                0
                                М̭̯̞͈̙̪̱ͅы̙̖̠̬͈͇̹̜́͢͞ ̷҉͚̭̩̣̭̤͉т̹̕͜͝у͙̘͠т̝̼̼̻̺͓ ̴̴҉̗͚͚̞̘͍в̮̪́с̡̧̦̮̰ё̵̧̱̻̺̦͇̠̖͕ ͓̩̟е̳̥͎̻̺̼̣̕щ͓̣͖е͚͙̝̫͖͙̖ ̻̲͍̫̖̫͜п̴̻͍̜̫͍̞̣̹р̴̧̭͉̀о͔̰̹͖͇͕̟͓ ̶̢̮͉д̢͈̖̭͎̗̳͔ͅи͏̙͎̞͍͖͞а̟̱̹͝к̙͕͕̼̪̹̯̙̫р̯͚̀ͅи̯̦͖͟т̸̣͎͈̭͚͉̺и̴̷̮̣̜̤͕͈̣к̪͎͚̙̠̖͎у͖̲̩͍̪̺̖ ͏҉̮̬̭̯͜и͓͟͜ ̸͕͙͠з̲͎͍͓͙н̴͎а͏͓к҉̠̲̣̺͍̠̯̱̮̕͝о҉҉͈̩̭͇̩̖̺м̢͍̖͓̜̪̮̲͚͝е̴̪͚̯̖͓с̬̗̩̪̝͔̙̤̕͢т̧̫̮͔͙̜̠̠ͅа̺̮̘͎̬ ̬͇г͏̬̙̲̘о̛͕̩̭̖͓в͓͕͓͎̣͍͉̰̕͟о̛̞͔͉р̸͍̤͚͠и͇̦̼̪̖͖͕м̡̤̭̱̘͚,҉͔̤͔͙͞ ̡̢̹̗̠͍̘̳ͅͅд̘͕͖̪̠̥̫̺͘а̶̳̭͓̥͜͠?̷̸҉̘̼̪͍̙̗͎̼

                                  0
                                  А вот это говорит скорее о недостатках.
                                  0
                                  Перефразирую ваше утверждение: слово должно всегда умещаться в один «атом» строки. Если это не так, то смысла в длинном кодировании просто нет.

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

                                  Если мы даже возьмём русский без диакритических знаков (ё и й — самостоятельные буквы в русском), то есть знак ударения, который необходим для однозначного прочтения слова, и вы предлагаете ввести в UTF кодовые точки для всех гласных русского языка, которые могут быть ударными?

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

                                  А дизайнерам шрифта вы предлагаете явно описать все символы вместо автоматической композиции сложных символов из ряда простых?

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

                                  Максимальная длина символа в utf8 все равно ограничена. Почему бы не ввести что-то типа aligned utf8, в котором на каждое знакоместо будет зарезервировано максимально возможное количество байт. И использовать эту кодировку там, где важна атомарность символов. Сейчас ведь нет ни одной юникодной кодировки, где можно просто обратиться к символу по индексу, не боясь нарваться на отдельные точечки от «ё».
                                    0
                                    Не нужно передергивать.

                                    Гипербола.


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

                                    Носители неалфавитных языков не согласны. И в следующем параграфе приведён пример-отсылка: почему вы его проигнорировали?


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

                                    Использование ударения обязательно для передачи слова при наличии форм, отличающихся только ударением (особенно там, где нет контекста для идентификации конкретного слова).


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

                                    Зачем вы хотите увеличить объём данных там, где данные могут быть вычислены достаточно быстро из меньшего объёма? У нас вот уже три десятилетия проблемы со скоростью доступа к данным: вычислить быстрее часто.


                                    Максимальная длина символа в utf8 все равно ограничена.

                                    Она гораздо менее ограничена, чем пространство Unicode на данный момент.


                                    Почему бы не ввести что-то типа aligned utf8, в котором на каждое знакоместо будет зарезервировано максимально возможное количество байт.

                                    Всё давно придумано: это называется UCS-4. UTF-8 появился после UCS-2 и UCS-4 для целей экономии. UTF-кодировки — это сжатые варианты первых.


                                    Сейчас ведь нет ни одной юникодной кодировки, где можно просто обратиться к символу по индексу, не боясь нарваться на отдельные точечки от «ё».

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


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


                                    И вы не указали, зачем вам атомарность символов?


                                    P.S. Я не поклонник Unicode (ISO 10646), мне больше импонирует подход ISO 646. Например, имея набор строк в любой юникодной кодировке, вы не сможете произвести их сортировку: правила сортировки в разных языках разные. Далеко за примерами ходить не нужно: взять, хотя бы, символы с акцентами в немецком и шведском. В ISO 646 же у нас есть информация о языке текста.

                                    +1
                                    ..., то есть знак ударения, ...

                                    Вот, кстати, пример неоднозначного прочтения без ударения: «то́ есть» vs «то е́сть» — с указанием фразового ударения проще прочитать правильно. Первый вариант употребляется чаще — можно продолжать писать без ударения, а вот во втором случае (что имеет место быть в цитируемом тексте) лучше писать с ударением, тем более в компьютерную эру, когда это сделать довольно просто.


                                    Hint для пользователей *nix: <compose> + <апостроф> + <буква> — после ввода апострофа можно переключать раскладку.

                                0
                                Ну как-нибудь возьмите многотерабайтную базу, потом пересоздайте её в UTF-32. Посмотрите сколько понадобится добавить места на каждой реплике (учтите, что кроме таблиц есть ещё и индексы), как изменится скорость чтения и записи.
                                  0
                                  Сколько в вашей многотерабайтной базе занимает именно текст? Сколько не видал больших баз, текста там было дай бог процентов 5-10.
                                    0
                                    А будет 15-30
                                      0
                                      Зато поиск по тексту будет летать.
                                        0
                                        Нет, не будет, то есть, будет работать плюс-минус так же
                                        0
                                        И что изменит рост размера на 10-20%? В любой живой базе свободных страниц в разы больше, так что размер почти не изменится.
                                          0
                                          Так количество свободных страниц также пропорционально увеличится, так что «почти не изменится» — на те же 10-20%.
                                          А изменится, в общем случае, стоимость системы хранения
                                    +1
                                    Длину строки без полного парсинга определить невозможно.

                                    А что такое длина строки? Число символов в строке? А зачем они вам? В человеческих языках символы, часто, совсем не одной и той же ширины: и вот, вам снова нужно разобрать всю строку, чтобы вычислить её отображаемую длину, независимо от кодирования — что UTF-8, что UCS-4.

                                      0

                                      Дополнение: забыл добавить упоминание кернинга.

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

                                          Она была элементарной, потому что не отражала сложности реального мира: "ахаха, у нас в дефолт кантри одна латиница, одна раскладка клавиатуры, один порядок сортировки и вообще 7 бит хватит на все случаи жизни".

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

                                Самое читаемое