Про що нормалізується UTF-8?


129

Проект ICU (який також тепер має бібліотеку PHP ) містить класи, необхідні для нормалізації рядків UTF-8, щоб полегшити порівняння значень під час пошуку.

Однак я намагаюся розібратися, що це означає для додатків. Наприклад, у яких випадках я хочу "Канонічну еквівалентність" замість "Еквівалентність сумісності" чи навпаки?


230
Хто ̸͢k̵͟n̴͘ǫw̸̛s͘ w͘͢ḩ̵a҉̡͢t жахи Лежи в Темне серце Юникода ͞
ObscureRobot

@ObscureRobot Я дуже хочу знати, чи можуть ці додаткові символи мати стани чи ні
eonil

1
@Eonil - я не впевнений, що означає стан у контексті Unicode.
ObscureRobot

@ObscureRobot Наприклад, деякий код точки , як це: (begin curved line) (char1) (char2) … (charN) (end curved line)а не так: (curved line marker prefix) (char1) (curved line marker prefix) (char2) (curved line marker prefix) (char2). Іншими словами, мінімальна одиниця, яку можна надати?
eonil

2
Це звучить як гарне питання самостійно.
ObscureRobot

Відповіді:


181

Все, чого ти ніколи не хотів знати про нормалізацію Unicode

Канонічна нормалізація

Unicode включає кілька способів кодування деяких символів, особливо помітних символів. Канонічна нормалізація змінює кодові точки на канонічну форму кодування. Отримані в результаті кодові точки повинні бути ідентичними вихідним, що забороняють будь-які помилки в шрифтах або системі відображення.

Коли використовувати

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

Канонічна нормалізація буває 2 форм: NFD і NFC. Дві рівнозначні в тому сенсі, що можна конвертувати між цими двома формами без втрат. Порівняння двох рядків під NFC завжди дасть той самий результат, що і порівняння їх за NFD.

НФД

У NFD символи повністю розширені. Це швидша форма нормалізації для обчислення, але результати в більшій кількості кодових точок (тобто використовується більше місця).

Якщо ви просто хочете порівняти два рядки, які вже не нормалізовані, це найкраща форма нормалізації, якщо ви не знаєте, що вам потрібна нормалізація сумісності.

NFC

NFC рекомбінує кодові точки, коли це можливо після запуску алгоритму NFD. Це займе трохи більше часу, але призводить до коротших рядків.

Нормалізація сумісності

У Unicode також входить багато символів, які дійсно не належать, але були використані у застарілих наборах символів. Unicode додав їх, щоб дозволити обробляти текст у цих наборах символів як Unicode, а потім конвертувати назад без втрат.

Нормалізація сумісності перетворює їх у відповідну послідовність "реальних" символів, а також виконує канонічну нормалізацію. Результати нормалізації сумісності можуть бути не ідентичними оригіналам.

Символи, які включають інформацію про форматування, замінюються на такі, які не мають. Наприклад, персонаж перетворюється на 9. Інші не передбачають відмінностей у форматуванні. Наприклад, римський цифровий символ перетворюється на звичайні букви IX.

Очевидно, що після цього перетворення буде здійснено безперервне перетворення назад у початковий набір символів.

Коли використовувати

Консорціум Unicode пропонує думати про нормалізацію сумісності як про ToUpperCaseперетворення. Це щось, що може бути корисно в деяких обставинах, але ви не повинні просто застосовувати його мимоволі.

Відмінним випадком використання буде пошукова система, оскільки ви, ймовірно, хочете, щоб пошук 9відповідав .

Одне, що ви, мабуть, не повинні робити, - це відображати результат застосування нормалізації сумісності до користувача.

NFKC / NFKD

Форма нормалізації сумісності складається у двох формах NFKD і NFKC. Вони мають ті ж стосунки, що і між NFD та C.

Будь-яка рядок у NFKC притаманна також NFC і однакова для NFKD та NFD. Таким чином NFKD(x)=NFD(NFKC(x)), і NFKC(x)=NFC(NFKD(x))т.д.

Висновок

Якщо сумніваєтеся, перейдіть до канонічної нормалізації. Вибирайте NFC або NFD, виходячи з можливих компромісів простір / швидкість або на основі того, що вимагається тим, з чим ви взаємодієте.


42
Швидкий посилання, щоб запам'ятати, що означає абревіатури: NF = нормалізована форма D = розкласти (розпарити) , C = скласти (стиснути) K = сумісність (оскільки "C" прийнято).
Майк Спросс

12
Ви завжди хочете, щоб NFD всі рядки на вході були як перше, а NFC всі рядки виводилися як остання річ. Це добре відомо.
tchrist

3
@tchrist: Це, як правило, хороша порада, за винятком тих випадків, коли ви хочете, щоб вихід був байтом для ідентичного входу, коли не було внесено змін. Є деякі інші випадки, коли вам потрібно NFC в пам'яті або NFD на диску, але це швидше, ніж правило.
Кевін Кеткарт

@Kevin: Так, NFD і NFC вийдуть знищити синглів. Я не впевнений, що хтось про це піклується, але можливо.
tchrist

2
Ви можете подумати про це, але з додатку: "Щоб перетворити рядок Unicode у задану форму нормалізації Unicode, перший крок - це повністю розкласти рядок". Таким чином, навіть коли ми будемо керувати NFC, Q-Caron спочатку став Q + Caron, і не міг його запропонувати, оскільки правила стабільності забороняють додавати нове складання карти. NFC ефективно визначається як NFC(x)=Recompose(NFD(x)).
Кевін Кеткарт

40

Деякі символи, наприклад, букву з наголосом (скажімо, é), можна представити двома способами - єдиною кодовою точкою U+00E9або простою літерою, за якою слід поєднувати знак наголосу U+0065 U+0301. Звичайна нормалізація вибере одне із них, щоб завжди представляти його (єдина кодова точка для NFC, поєднуюча форма для NFD).

Для символів, які можуть бути представлені кількома послідовностями базових символів та комбінуючими позначками (скажімо, "s, крапка внизу, крапка вгорі" проти поставлення крапки вище, ніж точка нижче, або використання базового символу, у якого вже є одна з крапок), NFD буде також виберіть один із них (нижче йде перший, як це відбувається)

Декомпозиції сумісності включають в себе ряд символів, які "насправді не повинні" бути символами, але є тому, що вони використовувались у застарілих кодуваннях. Звичайна нормалізація не уніфікує їх (для збереження цілісності в обидва кінці - це не є проблемою для комбінуючих форм, оскільки жодне застаріле кодування (крім кількох в'єтнамських кодувань) не використовує обох), але нормалізація сумісності буде. Подумайте, як кілограмовий знак «кг», який з’являється в деяких східноазіатських кодировках (або напівширока / повна ширина катакана і алфавіту), або «фі» лігатура в Макромані.

Докладнішу інформацію див. У розділі http://unicode.org/reports/tr15/ .


1
Це справді правильна відповідь. Якщо ви використовуєте просто канонічну нормалізацію тексту, що виник у деякому застарілому наборі символів, результат можна без втрат перетворити назад у цей набір символів. Якщо ви використовуєте декомпозицію сумісності, ви закінчуєтесь без будь-яких символів сумісності, але перетворити назад у початковий набір символів вже неможливо без втрат.
Кевін Кеткарт

13

Нормальні форми (Unicode, а не бази даних) стосуються переважно (виключно?) Символів, які мають діакритичні позначки. Unicode надає деяким символам "вбудовані" діакритичні позначки, такі як U + 00C0, "Латинська столиця A з могилою". Той самий символ може бути створений з `Latinski Capital A '(U + 0041) з" Combining Grave Accent "(U + 0300). Це означає, що навіть якщо дві послідовності створюють один і той же результуючий символ, байт-байт порівняння покаже їх як абсолютно різні.

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

У цьому випадку "сумісність" означає сумісність з кодом, який передбачає, що одна кодова точка дорівнює одному символу. Якщо у вас є такий код, ви, ймовірно, хочете використовувати звичайну форму сумісності. Хоча я ніколи не бачив, щоб це було прямо зазначено, назви звичайних форм означають, що консорціум Unicode вважає за краще використовувати окремі комбінуючі діакритичні позначки. Для цього потрібно більше інтелекту для підрахунку фактичних символів у рядку (а також таких речей, як інтелектуальне розбиття рядка), але є більш універсальним.

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


Отже, це та частина, в яку вступають тоді функції графеми . Мало того, що символ має більше байтів, ніж ASCII - але декілька послідовностей можуть бути одним символом, чи не так? (На відміну від функцій рядка MB .)
Xeoncross

4
Ні, "одна кодова точка є одним символом" приблизно відповідає NFC (той, який поєднує позначки - NFD, і жоден з них не є "сумісністю"). - Нормалізація сумісності NFKC / NFKD - це інше питання; сумісність (або її відсутність) для застарілих кодувань, які, наприклад, мали окремі символи для грецької mu та 'micro' (це приємно виховувати, оскільки версія "сумісності" є тією, що є в латинській 1 блоці)
Random832

@ Random832: На жаль, цілком правильно. Я повинен знати краще, ніж йти з пам’яті, коли я не працював з ним останній рік-два.
Джеррі Труну

@ Random832 Це неправда. Ваша «приблизно» там занадто. Розглянемо дві графеми, ō̲̃ і ȭ̲. Існує багато способів написання кожного з тих, з яких саме один - це NFC і один NFD, але існують і інші. Не випадково лише один код. NFD для першого є "o\x{332}\x{303}\x{304}", а NFC є "\x{22D}\x{332}". Для другого NFD є, "o\x{332}\x{304}\x{303}"а NFC є "\x{14D}\x{332}\x{303}". Однак існує чимало неканонічних можливостей, які канонічно відповідають цим. Нормалізація дозволяє двійковим порівнянням канонічно еквівалентних графем.
tchrist

5

Якщо два рядки Unicode канонічно еквівалентні, то рядки дійсно однакові, лише використовуючи різні послідовності unicode. Наприклад, Ä може бути представлений або за допомогою символу Ä, або комбінації A і ◌̈.

Якщо рядки є лише еквівалентною сумісністю, то рядки необов'язково однакові, але вони можуть бути однаковими в деяких контекстах. Наприклад, ff можна вважати таким же, як і ff.

Отже, якщо ви порівнюєте рядки, ви повинні використовувати канонічну еквівалентність, оскільки еквівалентність сумісності не є реальною еквівалентністю.

Але якщо ви хочете сортувати набір рядків, може бути доцільним використання еквівалентності сумісності, оскільки вони майже однакові.


5

Це насправді досить просто. UTF-8 насправді має кілька різних уявлень одного і того ж "характеру". (Я використовую символи в лапках, оскільки байтові вони різні, але практично вони однакові). Приклад наведено у пов'язаному документі.

Символ "Ç" може бути представлений у вигляді послідовності байтів 0xc387. Але він також може бути представлений C(0x43) з наступною послідовністю байтів 0xcca7. Тож можна сказати, що 0xc387 та 0x43cca7 - це один і той же персонаж. Причина, яка спрацьовує, полягає в тому, що 0xcca7 - це поєднувальний знак; це означає, що він приймає персонажа перед ним (a Chere) і модифікує його.

Тепер, що стосується різниці між канонічною еквівалентністю та еквівалентністю сумісності, нам потрібно переглянути загальні символи.

Існує 2 типи символів, ті, які передають значення через значення , і ті, які приймають інший символ і змінюють його. 9 - значущий персонаж. Супер-сценарій ⁹ приймає це значення і змінює його поданням. Так канонічно вони мають різні значення, але вони все ще представляють базовий характер.

Канонічна еквівалентність - це те, коли послідовність байтів надає той самий символ із тим самим значенням. Еквівалентність сумісності - це те, коли послідовність байтів надає інший символ з тим самим базовим значенням (навіть якщо він може бути змінений). 9 і ⁹ є еквівалентом сумісності, оскільки вони обидва означають "9", але не є канонічно еквівалентними, оскільки не мають однакового представлення.


@tchrist: Прочитайте ще раз відповідь. Я ніколи навіть не згадував різні способи представлення однієї і тієї ж кодової точки. Я сказав, що існує кілька способів представити один і той же друкований символ (через комбінатори та кілька символів). Що стосується і UTF-8, і Unicode. Тож ваші відгуки та коментарі насправді взагалі не стосуються того, що я сказав. Насправді я в основному робив те саме, що тут робив головний плакат (хоча і не дуже) ...
ircmaxell

4

Чи буде більш доречним для вас канонічна еквівалентність чи сумісність, залежить від вашої заявки. Спосіб роздуму ASCII про порівняння рядків приблизно орієнтується на канонічну еквівалентність, але Unicode представляє багато мов. Я не думаю, що можна припустити, що Unicode кодує всі мови таким чином, що дозволяє поводитися з ними так само, як західноєвропейський ASCII.

На малюнках 1 і 2 є хороші приклади еквівалентності двох типів. Згідно з еквівалентністю сумісності, схоже, що однакове число у формі суб- та супер-скрипту порівняно б рівним. Але я не впевнений, що вирішить ту саму проблему, що і курсивна арабська форма або обертаються символи.

Важка правда обробки тексту Unicode полягає в тому, що вам потрібно глибоко задуматися над вимогами щодо обробки тексту вашої програми, а потім вирішити їх, як можна, за допомогою доступних інструментів. Це безпосередньо не стосується вашого питання, але більш детальна відповідь потребує мовних експертів для кожної з мов, які ви очікуєте на підтримку.


1

Проблема порівняння рядків : два рядки із вмістом, еквівалентним для більшості програм, можуть містити різні послідовності символів.

Див . Канонічну еквівалентність Unicode : якщо алгоритм порівняння простий (або повинен бути швидким), еквівалент Unicode не виконується. Ця проблема виникає, наприклад, у канонічному порівнянні XML, див. Http://www.w3.org/TR/xml-c14n

Щоб уникнути цієї проблеми ... Який стандарт використовувати? "розширений UTF8" або "компактний UTF8"?
Використовувати "ç" або "c + ◌̧."?

W3C та інші (наприклад, імена файлів ) пропонують використовувати "складені як канонічні" (майте на увазі C "найкомплектніших" коротших рядків) ... Отже,

Стандарт - C ! сумнівайтеся, використовуйте NFC

Для інтероперабельності та для вибору "конвенції щодо конфігурації" рекомендацією є використання NFC для "канонізації" зовнішніх рядків. Наприклад, для зберігання канонічного XML, наприклад, зберігайте його у "FORM_C". CSV W3C у Веб-робочій групі також рекомендує NFC (розділ 7.2).

PS: de "FORM_C" - це форма за замовчуванням у більшості бібліотек. Вих. в нормалізаторі PHP.isnormalized () .


Термін " композиційна форма" ( FORM_C) використовується для обох, щоб сказати, що "рядок знаходиться в С-канонічній формі" (результат перетворення NFC) і щоб сказати, що використовується алгоритм перетворення ... Див. Http: //www.macchiato.com/unicode/nfc-faq

(...) кожна з наступних послідовностей (перші дві є односимвольними послідовностями) представляють один і той же символ:

  1. U + 00C5 (Å) ЛАТИНСЬКИЙ КАПІТАЛ ЛІТИН А З КОЛЬЦОМ НАД
  2. U + 212B (Å) АНГСТРОМ ЗНАЧ
  3. U + 0041 (A) ЛІТИНСЬКИЙ КАПІТАЛЬНИЙ ПІСЛЯ A + U + 030A (̊) ОБ'ЄДНАННЯ КОЛЬКОГО ПРОБЛЕМУ

Ці послідовності називаються канонічно еквівалентними. Перша з цих форм називається NFC - для форми нормалізації C, де C - для композиції . (...) Функція, що перетворює рядок S у форму NFC, може бути скорочена як toNFC(S), а функція , яка перевіряє, чи є S у NFC, скорочується як isNFC(S).


Примітка: для перевірки нормалізації невеликих рядків (чисті посилання UTF-8 або XML-сутності) ви можете використовувати цей тест / нормалізувати онлайн-конвертер .


Я збентежений. Я зайшов на сторінку онлайн-тестувальника і там ввожу: "TÖST MÉ pleasé." і спробуйте всі 4 заданих нормалізацій - жодна зміна мого тексту жодним чином не змінюється, за винятком того, що він змінює коди, які використовуються для подання цих символів. Я помилково вважаю, що "нормалізація" означає "видалити всі діакритики та подібні", а це насправді означає - просто змінити кодування utf внизу?
userfuser

Привіт @userfuser, можливо, вам потрібна позиція щодо програми: це порівняти чи стандартизувати текст? Мій пост тут стосується лише "стандартизації" програм. PS: коли весь світ використовує стандарт, проблема порівняння зникає.
Пітер Краус
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.