Как стать автором
Обновить

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

Пару раз в жизни встречал тексты, где вместо буквы «й» было сочетание «и + ˜». Всякий раз хотелось пожелать мучительной смерти создателю такой подрянки.
Мне многократно попадались на этом сайте и тексты, и комментарии, где «ы» было записано парой «ь»-«i». Так и не понял что это. Подавление поиска?
Мало того, в этом подавлении поиска есть украинский след.

В украинской раскладке — сюрприз! — нет буквы «ы», потому что в украинском этот звук обозначается буквой «и».

Украина

мак

Это маководам лучи добра.
Apple старались.

Стандартная ситуация для кросс-платформы, где фигурирует мак.
Самое типичное — файлы с русскими именами на NFC-шаре между маком и линуксом. И именно с теми самыми двумя буквами в имени — ё и й. Внезапно обнаруживается, что записанные из линукса такие файлы мак не видит. Зато может сам записать такие, причём БЕЗ диалога о существующем файле и его замене.
И после всего этого ты смотришь на два файла с одинаковым именем и думаешь, WTF?


История реальная; набираю нотные партитуры в лилипонде; попеременно то с убунты, то с макбука...

Хотя в вышеприведённом примере две строки выглядят абсолютно одинаково, то, как они представлены в системе, те байты, в виде которых они сохранены на диске, различаются.

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


Почему существуют и тот и другой стандарты? Дело в том, что тексты на западных языках обычно эффективнее всего кодируются с использованием стандарта UTF-8.

Если бы дело было только в этом, то UTF-16 не использовался бы в большинства JavaScript-интерпретаторов. Просто при передаче важнее компактность (а UTF-8 в целом получается компактнее), а при работе со строками важнее скорость определения длины строки и положения символа в заданной позиции.

Если бы было так, то использовали бы UTF-32. Использование же UTF-16 — это по большей части легаси.

Юникод совсем недавно прорвало за границы 2-байтной кодировки. Точнее, стандарт предполагал это давно, но массовое использование этих символов началось совсем недавно.

И после "прорыва" ситуация стала печальной: UTF-16 объединяет недостатки UTF-8 и UTF-32.
Он занимает много места (кроме иероглифов) и является кодировкой, где каждый code point кодируется последовательностью байт переменной длины ("спасибо" суррогатным парам). К тому же, старый и не очень софт не поддерживает UTF-16 (только UCS-2). Плюс, проблемы с endianness.
Поэтому, в современном софте имеет смысл использовать UTF-8 для хранения текста, UTF-32 для обработки, а про UTF-16 просто забыть.

Зависит от задачи. Если вам не нужна работа с текстом на уровне кодпоинтов (или вообще букв), то нет никакого смысла тратить время на перекодировку и память на хранение в менее компактном формате. Например парсить JSON, XML, HTML можно одинаково эффективно и в UTF-8, и в UTF-16 и в UTF-32.
> UTF-32 для обработки

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

Иначе в общем случае использование 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. Просто пока вы не работаете с суррогатными парами (эмодзи всякие), вы этого не замечаете.

Никогда не понимал смысла втягивать в кодировки эмодзи. Зачем? 137 тысяч эмодзи!
137 тысяч — это общее количество символов в текущем Юникоде (версия 12.0). Из них эмодзи «всего» лишь 1273.
согласен, картинок с сумочками, котиками и пивными кружками можно придумать бесконечное количество, даже диапазона юникода не хватит, зачем было сувать их в стандарт

А может кто-нибудь пояснить смысл неоднозначного представления символа? Если это один и тот же символ, это выглядит как отвратительное решение. Если разные, то не должно быть нормализаций.
//тут же вопрос — о и o — одна и та же буква? :)

разные, так же как с и c
console

Это наследие однобайтовых кодировок. В таких кодировках можно записать не более 256 символов, поэтому приходилось выкручиваться, создавая отдельные символы для частей букв (u, ¨) и затем соединяя их в буквы.

Я первый раз услышал про такое соединение именно в контексте Юникода. Правда есть такие кодировки? В Windows-1252, например, все символы с умляутами отдельно: en.wikipedia.org/wiki/Windows-1252#Character_set

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


This feature was introduced in the standard to allow compatibility with preexisting standard character sets, which often included similar or identical characters

Возможно, я перепутал понятия «кодировка» и «набор символов».

В 7 битном ASCII умляутов нет, а для верхних 128 символов были на каждом компе три разных кодировки. В итоге в Европах просто на какое-то время отказались от умляутов в целях совместимости, чтобы условное письмо могли прочесть в соседней деревне.
Я первый раз услышал про такое соединение именно в контексте Юникода. Правда есть такие кодировки? В Windows-1252, например, все символы с умляутами уже соединены: en.wikipedia.org/wiki/Windows-1252#Character_set
Я первый раз услышал про такое соединение именно в контексте Юникода. Правда есть такие кодировки? В Windows-1252, например, все символы с умляутами уже соединены:
Прошу прощения, кто-нибудь, удалите лишние дубли, что-то пошло не так :(
Например, ударения.
Ещё всякий матан с гробиками и стрелочками со всех сторон символа.
Ещё есть языки с размытой нормой, например ss и ß в немецком это одна и то же «буква», но в одних регионах пишут так, а в других эдак.
Пример в заголовке статьи неудачный: конкретно Zoё может быть не Zоё, потому что буква «о» английская и русская используются.
Вот, кстати, интересный вопрос: а такая нормализация поможет сделать две такие строки равными?
trim().normalize().translit().toLowerCase()
Возможно я что-то путаю, но насколько я помню UTF-8 символ может состоять из максимум 6 байт а не 4.
Было дело, но они что-то поменяли в стандартах. Видимо нету смысла закладывать такое количество символов.
Эта проблема очень ярко проявлялась на iOS после перехода на APFS (начиная с iOS 10.3 и вплоть до iOS 11.0, в которой проблема была решена, более полугода спустя). Много нервов помотали пользователи которые через iTunes добавляли файлы в папку документов приложения, приложение файлы видело но не могло открыть. Причем у некоторых пользователей все работало, а у некоторых ничего не получалось. В итоге выяснилось, что проблема возникает с файлами, загруженными из iTunes из под Windows для файлов имеющих в названии символы Й, Ё ну и всякие умляуты и подобное. Причем у пользователей на OSX такой проблемы небыло. Пользователям рекомендовал переименовывать файлы таким образом, чтобы в названии были только латинские символы и цифры. Но виноват, в глазах пользователей, всеравно был разработчик приложения. Сильно тогда слили рейтинг приложения из за этой проблемы.
Правильно ли я понял, что проблема возникает только в том случае, когда кодировки отличаются?
Век живи, век учись.
const str = '\u0065\u0301'
console.log(str == '\u00e9') // => false
const normalized = str.normalize('NFC')
console.log(normalized == '\u00e9') // => true
console.log(normalized.length) // => 1


Некорректно. Нужно сравнивать не normalized и '\u00e9', а


const str1 = '\u0065\u0301'
const str2 = '\u00e9'

const normalized1 = str1.normalize('NFC')
const normalized2 = str2.normalize('NFC')

console.log(normalized1 == normalized2)
В PHP для этого используется класс Normalizer.

под капотом все используют icu.

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