Комментарии 40
мак
Стандартная ситуация для кросс-платформы, где фигурирует мак.
Самое типичное — файлы с русскими именами на NFC-шаре между маком и линуксом. И именно с теми самыми двумя буквами в имени — ё и й. Внезапно обнаруживается, что записанные из линукса такие файлы мак не видит. Зато может сам записать такие, причём БЕЗ диалога о существующем файле и его замене.
И после всего этого ты смотришь на два файла с одинаковым именем и думаешь, WTF?
История реальная; набираю нотные партитуры в лилипонде; попеременно то с убунты, то с макбука...
Хотя в вышеприведённом примере две строки выглядят абсолютно одинаково, то, как они представлены в системе, те байты, в виде которых они сохранены на диске, различаются.
Юникод вообще не про байты. Для ответа на вопрос из заголовка можно было просто опустить весь рассказ про кодировки и суррогатные пары, они никак не относятся к нормализации.
Почему существуют и тот и другой стандарты? Дело в том, что тексты на западных языках обычно эффективнее всего кодируются с использованием стандарта UTF-8.
Если бы дело было только в этом, то UTF-16 не использовался бы в большинства JavaScript-интерпретаторов. Просто при передаче важнее компактность (а UTF-8 в целом получается компактнее), а при работе со строками важнее скорость определения длины строки и положения символа в заданной позиции.
Если бы было так, то использовали бы UTF-32. Использование же UTF-16 — это по большей части легаси.
И после "прорыва" ситуация стала печальной: UTF-16 объединяет недостатки UTF-8 и UTF-32.
Он занимает много места (кроме иероглифов) и является кодировкой, где каждый code point кодируется последовательностью байт переменной длины ("спасибо" суррогатным парам). К тому же, старый и не очень софт не поддерживает UTF-16 (только UCS-2). Плюс, проблемы с endianness.
Поэтому, в современном софте имеет смысл использовать UTF-8 для хранения текста, UTF-32 для обработки, а про UTF-16 просто забыть.
А предварительная нормализация разве гарантирует, что каждый символ будет представлен одной кодовой точкой?
Иначе в общем случае использование UTF-32 не дает возможность произвольного доступа к символам и не избавляет от необходимости пробегать по всей строке, а памяти жрет чрезмерно.
UTF-32 даёт нам возможность более вычислительно более простого итерирования по кодовым точкам. При сборке символа из кодовых точек нам же всё равно придётся итерироваться по ним. А так как у UTF-32 фиксированое количество байт на точку, нам нужно будет заботиться только о смещении относительно начала, а не об обработке суррогатных пар.
Если бы дело было только в этом, то UTF-16 не использовался бы в большинства JavaScript-интерпретаторов. Просто при передаче важнее компактность (а UTF-8 в целом получается компактнее), а при работе со строками важнее скорость определения длины строки и положения символа в заданной позиции.
Определение длины строки в символах и позиции символа при использовании что UTF-8, что UTF-16 одинаково геморройно, потому что требует итерации по всей строке. Не забывайте, что в UTF-16 есть кодпоинты, которые представляются двумя кодюнитами.
Поэтому строки в JS — это последовательность UTF-16 code units, а не UTF-16 code points. Просто пока вы не работаете с суррогатными парами (эмодзи всякие), вы этого не замечаете.
А может кто-нибудь пояснить смысл неоднозначного представления символа? Если это один и тот же символ, это выглядит как отвратительное решение. Если разные, то не должно быть нормализаций.
//тут же вопрос — о и o — одна и та же буква? :)
Это наследие однобайтовых кодировок. В таких кодировках можно записать не более 256 символов, поэтому приходилось выкручиваться, создавая отдельные символы для частей букв (u
, ¨
) и затем соединяя их в буквы.
Я не знаю, в каких именно кодировках это используется. В той же википедии сказано, что возможность неоднозначного представления была добавлена для совместимости со старыми наборами символов:
This feature was introduced in the standard to allow compatibility with preexisting standard character sets, which often included similar or identical characters
Возможно, я перепутал понятия «кодировка» и «набор символов».
Ещё всякий матан с гробиками и стрелочками со всех сторон символа.
Ещё есть языки с размытой нормой, например ss и ß в немецком это одна и то же «буква», но в одних регионах пишут так, а в других эдак.
Когда «Zoë» !== «Zoë», или почему нужно нормализовывать Unicode-строки