В проекте ICU (который теперь также есть библиотека PHP ) содержатся классы, необходимые для нормализации строк UTF-8, чтобы упростить сравнение значений при поиске.
Тем не менее, я пытаюсь понять, что это означает для приложений. Например, в каких случаях я хочу «Каноническая эквивалентность» вместо «эквивалентности совместимости» или наоборот?
Unicode включает в себя несколько способов кодирования некоторых символов, в первую очередь акцентированных символов. Каноническая нормализация изменяет кодовые точки на каноническую форму кодирования. Результирующие кодовые точки должны быть идентичны исходным, запрещающим любые ошибки в шрифтах или движке рендеринга.
Поскольку результаты кажутся идентичными, всегда безопасно применять каноническую нормализацию к строке перед ее сохранением или отображением, если вы можете терпеть результат, не являющийся битом для бит, идентичным входу.
Каноническая нормализация происходит в двух формах: NFD и NFC. Эти два эквивалента в том смысле, что их можно преобразовать без потерь. Сравнение двух строк под NFC всегда даст тот же результат, что и сравнение с NFD.
У NFD символы полностью расширены. Это более быстрая форма нормализации для вычисления, но результаты в большем количестве кодовых точек (т. Е. Используют больше пространства).
Если вы просто хотите сравнить две строки, которые еще не нормализованы, это предпочтительная форма нормализации, если вы не знаете, что вам нужна нормализация совместимости.
NFC рекомбинирует кодовые точки, когда это возможно, после запуска алгоритма NFD. Это занимает немного больше времени, но приводит к сокращению строк.
Юникод также включает в себя множество символов, которые действительно не принадлежат, но были использованы в устаревших наборах символов. Unicode добавил их, чтобы текст в этих наборах символов обрабатывался как Unicode, а затем был преобразован обратно без потерь.
Нормализация совместимости преобразует их в соответствующую последовательность «реальных» символов, а также выполняет каноническую нормализацию. Результаты нормализации совместимости могут не совпадать с оригиналами.
Символы, содержащие информацию форматирования, заменяются символами, которые этого не делают. Например, символ ⁹
преобразуется в 9
. Другие не связаны с форматированием различий. Например, римский цифровой символ Ⅸ
преобразуется в обычные буквы IX
.
Очевидно, что после того, как это преобразование было выполнено, уже невозможно без потерь преобразовать обратно в исходный набор символов.
Консорциум Unicode предлагает думать о нормализации совместимости, как преобразование ToUpperCase
. Это может быть полезно в некоторых обстоятельствах, но вы не должны просто применять его волей-неволей.
Отличным вариантом использования будет поисковая система, так как вы, вероятно, захотите найти 9
для соответствия ⁹
.
Одна вещь, которую вы, вероятно, не должны делать, – это показать результат применения нормализации совместимости с пользователем.
Форма нормализации совместимости представлена в двух формах: NFKD и NFKC. Они имеют такое же отношение, что и между NFD и C.
Любая строка в NFKC присуща также в NFC, и то же самое для NFKD и NFD. Таким образом, NFKD(x)=NFD(NFKC(x))
и NFKC(x)=NFC(NFKD(x))
и т. Д.
Если вы сомневаетесь, идите с канонической нормировкой. Выберите NFC или NFD, основываясь на компромисс между пространством и скоростью, или на основе того, что требуется для чего-то, с чем вы взаимодействуете.
Некоторые символы, например буква с акцентом (скажем, é
), могут быть представлены двумя способами: одной кодовой точкой U+00E9
или простой буквой, за которой следует комбинационный знак акцента U+0065 U+0301
. Обычная нормализация выберет один из них, чтобы всегда представлять его (единая кодовая точка для NFC, комбинирующая форма для NFD).
Для символов, которые могут быть представлены несколькими последовательностями базовых символов и комбинацией меток (например, «s», «точка внизу», «точка выше»), вместо того, чтобы поставить точку выше точки ниже или использовать базовый символ, который уже имеет одну из точек), NFD будет также выберите один из них (ниже идет сначала, как это бывает)
Разделения совместимости включают в себя несколько символов, которые «на самом деле не должны» быть символами, но потому, что они использовались в устаревших кодировках. Обычная нормализация не будет унифицировать их (чтобы сохранить целостность «туда и обратно» – это не проблема для объединения форм, потому что не использовалось кодирование с устаревшим [кроме нескольких вьетнамских кодировок]), но нормализация совместимости будет. Подумайте, как «килограммовый» килограммовый знак, который появляется в некоторых восточноазиатских кодировках (или полуширине / полной ширине катаканы и алфавита), или «fi» лигатура в MacRoman.
Дополнительную информацию см. На странице http://unicode.org/reports/tr15/ .
Нормальные формы (Unicode, а не базы данных) имеют дело преимущественно (исключительно?) С символами, имеющими диакритические знаки. Unicode предоставляет некоторые символы с «встроенными» диакритическими знаками, такими как U + 00C0, «Latin Capital A with Grave». Один и тот же символ может быть создан из «Latin Capital A» (U + 0041) с «Объединением акцента на могилу» (U + 0300). Это означает, что даже если две последовательности создают один и тот же результирующий символ, байт по байт сравнение покажет им, что они совершенно разные.
Нормализация – попытка справиться с этим. Нормализация заверяет (или, по крайней мере, пытается), чтобы все символы были закодированы одинаково – либо все с использованием отдельного сочетания диакритической метки, где это необходимо, либо все, используя, по возможности, одну кодовую точку. С точки зрения сравнения, это не имеет большого значения, что вы выбираете – почти любая нормализованная строка будет правильно сравнивать с другой нормированной строкой.
В этом случае «совместимость» означает совместимость с кодом, который предполагает, что одна точка кода равна одному символу. Если у вас есть такой код, вы, вероятно, захотите использовать нормальную совместимость. Хотя я никогда не видел, чтобы это указывалось напрямую, названия нормальных форм подразумевают, что консорциум Unicode считает предпочтительным использовать отдельные комбинации диакритических знаков. Это требует большего интеллекта для подсчета фактических символов в строке (а также таких вещей, как разбиение строки разумно), но более универсальный.
Если вы в полной мере используете ОИТ, скорее всего, вы хотите использовать каноническую нормальную форму. Если вы пытаетесь самостоятельно написать код, который (например) предполагает, что точка кода равна символу, то вы, вероятно, хотите, чтобы нормальная совместимость делала это как можно чаще.
Если две строки в Юникоде канонически эквивалентны, строки действительно одинаковы, только с использованием разных последовательностей юникода. Например, Ä можно представить либо с использованием символа Ä, либо из комбинации A и ◌̈.
Если строки эквивалентны только эквивалентности, строки не обязательно одинаковы, но они могут быть одинаковыми в некоторых контекстах. Например, ff можно считать тем же, что и ff.
Итак, если вы сравниваете строки, вы должны использовать каноническую эквивалентность, поскольку эквивалентность совместимости не является реальной эквивалентностью.
Но если вы хотите отсортировать набор строк, имеет смысл использовать эквивалентность совместимости, так как они почти идентичны.
Независимо от того, подходит ли вам каноническая эквивалентность или эквивалентность совместимости, зависит от вашего приложения. ASCII-образ мышления о сравнении строк примерно соответствует канонической эквивалентности, но Unicode представляет много языков. Я не думаю, что можно с уверенностью предположить, что Unicode кодирует все языки таким образом, чтобы вы могли рассматривать их так же, как западноевропейский ASCII.
На рисунках 1 и 2 представлены хорошие примеры двух типов эквивалентности. В соответствии с эквивалентностью эквивалентности, похоже, что одинаковое число в суб- и супер-скриптовой форме будет сравниваться одинаково. Но я не уверен, что решает ту же проблему, что и в виде курсивной арабской формы или повернутых символов.
Жесткая правда обработки текста в Юникоде заключается в том, что вам нужно глубоко задуматься о требованиях к обработке текста приложения, а затем обратиться к ним так же, как и с доступными инструментами. Это напрямую не касается вашего вопроса, но для более подробного ответа потребуются лингвистические эксперты для каждого из языков, которые вы ожидаете поддерживать.
Это на самом деле довольно просто. UTF-8 фактически имеет несколько разных представлений одного и того же «символа». (Я использую символ в кавычках, так как по-разному они разные, но практически одинаковы). Пример приведен в связанном документе.
Символ «Ç» может быть представлен как последовательность байтов 0xc387. Но он также может быть представлен C
(0x43), за которым следует последовательность байтов 0x8ccca7. Таким образом, вы можете сказать, что 0xc387 и 0x438ccca7 являются одинаковыми символами. Причина, по которой это работает, заключается в том, что 0x8ccca7 является комбинированной меткой; то есть он принимает характер перед ним (здесь C
) и изменяет его.
Теперь, что касается разницы между канонической эквивалентностью и эквивалентностью совместимости, нам нужно посмотреть на символы в целом.
Есть два типа символов, которые передают смысл через значение , и те, которые принимают другого персонажа и меняют его. Итак, 9 – значащий персонаж. Супер-скрипт ⁹ принимает это значение и изменяет его путем представления. Таким образом, канонически они имеют разные значения, но они все еще представляют базовый характер.
Таким образом, каноническая эквивалентность – это то, где последовательность байтов передает один и тот же символ с тем же значением. Эквивалентность совместимости – это когда последовательность байтов создает другой символ с тем же базовым значением (даже если он может быть изменен). Таким образом, 9 и ⁹ эквивалентны совместимости, поскольку оба они означают «9», но не являются канонически эквивалентными, поскольку они не имеют одинакового представления …
Надеюсь, это поможет…
Проблема сравнения строк : две строки с содержанием, эквивалентные для большинства приложений, могут содержать разные последовательности символов.
См . Каноническую эквивалентность Unicode : если алгоритм сравнения прост (или должен быть быстрым), эквивалентность Unicode не выполняется. Эта проблема возникает, например, при каноническом сравнении XML, см. http://www.w3.org/TR/xml-c14n
Чтобы избежать этой проблемы … Какой стандарт использовать? «расширенный UTF8» или «компактный UTF8»?
Используйте «ç» или «c + ◌̧.»?
W3C и другие (например, имена файлов ) предлагают использовать «составленный как канонический» (учтите C «самых компактных» коротких строк) … Итак,
Для обеспечения совместимости и для выбора «конвенции по конфигурации» рекомендуется использовать NFC для «канонизации» внешних строк. Например, чтобы сохранить канонический XML, сохраните его в «FORM_C». CSV W3C в рабочей группе Web также рекомендует NFC (раздел 7.2).
PS: de "FORM_C" является формой по умолчанию в большинстве библиотек. Ex. в нормализаторе PHP.nnormalized () .
Термин « компостирующая форма» ( FORM_C
) используется для обоих, чтобы сказать, что «строка находится в C-канонической форме» (результат преобразования NFC) и сказать, что используется алгоритм преобразования … См. Http : //www.macchiato.com/unicode/nfc-faq
(…) каждая из следующих последовательностей (первые две являются односимвольными последовательностями) представляют один и тот же символ:
- U + 00C5 (Å) ЛАТИНСКОЕ КАПИТАЛЬНОЕ ПИСЬМО A С КОЛЬЦОМ ВЫШЕ
- U + 212B (Å) ANGSTROM SIGN
- U + 0041 (A) ЛАТИНСКОЕ КАПИТАЛ ПИСЬМО A + U + 030A (̊) КОМБИНИРОВАНИЕ КОЛЬЦА ВЫШЕ
Эти последовательности называются канонически эквивалентными. Первая из этих форм называется NFC – для формы нормализации C, где C – для компоста . (…) Функция, преобразующая строку S в форму NFC, может быть сокращена как
toNFC(S)
, тогда какtoNFC(S)
, которая проверяет, является ли S в NFC, сокращена какisNFC(S)
.
Примечание. Чтобы проверить нормализацию небольших строк (чистые ссылки UTF-8 или XML-сущности), вы можете использовать этот тест / нормализовать онлайн-конвертер .