У чому полягає нормалізація UTF-8?

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

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

116
@ ObscureRobot Я дійсно хочу знати, чи можуть ці додаткові символи мати статус чи ні
додано Автор Eonil, джерело
@ObscureRobot Наприклад, деяка кодова позиція виглядає так: скоріше замість цього: (почати вигнутий рядок) (char1) (char2) ... (charN) (кінець криволінійного рядка) : (префікс маркеру кривої лінії ) (char1) (префікс маркеру кривої лінії) (char2) (префікс марку кривої лінії) (char2) . Іншими словами, мінімальна одиниця, яку можна представити?
додано Автор Eonil, джерело
Це звучить як гарне питання самостійно.
додано Автор ObscureRobot, джерело
@ Еніл - Я не знаю, що означає держава в контексті юнікоду.
додано Автор ObscureRobot, джерело
Хто ̸͢k̵͟n̴͘ǫw̸̛s͘ w͘͢ḩ̵a҉̡͢t жахи Лежите в Темному серце Юнікод? ͞
додано Автор ObscureRobot, джерело

7 Відповіді

Все, що ви ніколи не хотіли знати про нормування Unicode

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

Unicode includes multiple ways to encode some characters, most notably accented characters. Канонічна нормалізація changes the code points into a canonical encoding form. The resulting code points should appear identical to the original ones barring any bugs in the fonts or rendering engine.

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

Because the results appear identical, it is always safe to apply Канонічна нормалізація to a string before storing or displaying it, as long as you can tolerate the result not being bit for bit identical to the input.

Канонічна нормалізація comes in 2 forms: НФД and NFC. The two are equivalent in the sense that one can convert between these two forms without loss. Comparing two strings under NFC will always give the same result as comparing them under НФД.

НФД

НФД has the characters fully expanded out. This is the faster normalization form to calculate, but the results in more code points (i.e. uses more space).

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

NFC

NFC recombines code points when possible after running the НФД algorithm. This takes a little longer, but results in shorter strings.

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

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

Нормалізація сумісності converts these to the corresponding sequence of "real" characters, and also performs Канонічна нормалізація. The results of Нормалізація сумісності may not appear identical to the originals.

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

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

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

The Unicode Consortium suggests thinking of Нормалізація сумісності like a ToUpperCase transform. It is something that may be useful in some circumstances, but you should not just apply it willy-nilly.

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

One thing you should probably not do is display the result of applying Нормалізація сумісності to the user.

НФКЦ/НФКД

Нормалізація сумісності form comes in two forms NFKD and NFKC. They have the same relationship as between НФД and C.

Any string in NFKC is inherently also in NFC, and the same for the NFKD and НФД. Thus NFKD(x)=НФД(NFKC(x)), and NFKC(x)=NFC(NFKD(x)), etc.

Висновок

If in doubt, go with Канонічна нормалізація. Choose NFC or НФД based on the space/speed trade-off applicable, or based on what is required by something you are inter-operating with.

165
додано
Ви завжди хочете, щоб всі рядки на вході були в першу чергу NFD, і всі вихідні рядки NFC - це саме останнє. Це добре відомо.
додано Автор tchrist, джерело
@Kevin: Так, NFD в NFC і винищувач знищить синглет. Я не впевнений, що хтось турбується про них, але можливо.
додано Автор tchrist, джерело
Швидке посилання на пам'ять, що означають абревіатури: NF = нормалізована форма D = розкласти (розпакувати) C = скласти (стиснути) K = сумісність (з "C").
додано Автор Mike Spross, джерело
"Порівняння двох рядків під NFC завжди матиме такий самий результат, як їх порівняння за NFD", але відповідно до розділ стійкості до нормалізації "[...], якщо рядок, який не має жодних непризначених символів, нормується під однією версією Unicode, він повинен залишатися нормованим у всіх майбутніх версіях Unicode". Тому, якщо Q-caron вводиться в більш пізній версії, і ви намагаєтеся порівняти рядок Q + caron з рядком Q-caron, форма NFC не буде еквівалентною, але форма NFD повинна. Це так?
додано Автор Aurimas, джерело
@christ: Це, як правило, хороша порада, за винятком тих випадків, коли ви хочете, щоб вихідний байт був байтом, ідентичним входу, коли не було внесено жодних змін. Є й інші випадки, коли ви хочете мати NFC в пам'яті або NFD на диску, але це виключення, а не правило.
додано Автор Kevin Cathcart, джерело
Ви можете це подумати, але з додатка: "Щоб перетворити рядок Unicode у задану форму унікального форматування Unicode, першим кроком буде повністю розбити рядок". Таким чином, навіть якщо ми використовуємо NFC, Q-Caron спочатку став Q + Caron і не зміг перебудувати, оскільки правила стабілізації забороняють додавати нову структуру зіставлення. NFC ефективно визначається як NFC (x) = Rekompose (NFD (x)) .
додано Автор Kevin Cathcart, джерело

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

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

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

Для отримання додаткової інформації див http://unicode.org/reports/tr15/ .

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

Нормальні форми (з Unicode, а не з баз даних) стосуються перш за все (виключно?) З символами, що мають діакритичні позначки. Unicode забезпечує деякі символи з "вбудованими" діакритичними знаками, такими як U + 00C0, "Latin Capital A with Grave". Цей символ може бути створений з "Latin Capital A" (U + 0041) з "Combining Grave Accent" (U + 0300). Це означає, що навіть якщо дві послідовності створюють один і той же результуючий символ, байт за байтом порівняння покаже їх як абсолютно інше.

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

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

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

13
додано
Отже, це те частина, в якій тоді потрапляють графемні функції . Мало того, що символ більше байтів, ніж ASCII, - але кілька послідовностей може бути одним символом праворуч? (На відміну від функцій рядок MB .)
додано Автор Xeoncross, джерело
@ 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, джерело
@ Random832: На жаль, правильно. Я повинен знати краще, ніж відмовитися від пам'яті, коли я не працював з ним протягом останнього року чи двох.
додано Автор Jerry Coffin, джерело
Ні, "одна кодова точка є одним символом" приблизно відповідає NFC (той, з яким об'єднуються позначки, - це NFD, і жодна з них не є "сумісністю"); - Нормалізація сумісності NFKC/NFKD - це інша проблема; сумісність (або її відсутність) для застарілих кодувань, наприклад, мали окремі символи для грецького мю та "мікро" (це весело, щоб виховати, тому що версія "сумісності" - така, яка входить до латинського блоку 1)
додано Автор Random832, джерело

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

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

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

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

5
додано

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

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

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

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

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

Надія, що допомагає ...

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

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

Figures 1 and 2 provide good examples of the two types of equivalence. Under compatibility equivalence, it looks like the same number in sub- and super- script form would compare equal. But I'm not sure that solve the same problem that as the cursive arabic form or the rotated characters.

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

4
додано

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

See Unicode's canonical equivalence: if the comparison algorithm is simple (or must be fast), the Unicode equivalence is not performed. This problem occurs, for instance, in xml canonical comparison, see http://www.w3.org/TR/xml-c14n

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

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

The standard is C! in doubt use NFC

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

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


Ther term "compostion form" (FORM_C) is used to both, to say that "a string is in the C-canonical form" (the result of a NFC transformation) and to say that a transforming algorithm is used... See http://www.macchiato.com/unicode/nfc-faq

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

     
      
  1. U + 00C5 (Å) ЛАТИНСКИЙ КАПІТАЛ ЛИСТА З КІЛЬКОМ ВИЩЕ
  2.   
  3. U + 212B (Å) ANGSTROM SIGN
  4.   
  5. U + 0041 (A) ЛАТИНСЬКИЙ КАПІТАЛЬНИЙ ЛИСТ A + U + 030A (̊) КОМБІНЮВАННЯ КІЛЬКІ ВЕРШИНИ
  6.   
     

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


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

1
додано
Привіт @userfuser, можливо, вам потрібна позиція щодо програми: чи порівняти або стандартизувати свій текст? Мій пост тут лише про "стандартизацію" програм. PS: коли весь світ використовує стандарт, проблема порівняння зникає.
додано Автор Peter Krauss, джерело
Я збентежений. Я пішов на цю сторінку тестувальника в Інтернеті і ввожу там: "TÖST MÉ pleasé." і спробуйте всі 4 з заданих нормалізацій - ніхто не змінює мого тексту ніяким чином, ну хіба що він змінює коди, що використовуються для подання цих символів. Я неправильно думаю, що "нормалізація" означає "видалити всі діакритичні та аналогічні", і це насправді означає - просто змінити utf кодування внизу?
додано Автор userfuser, джерело
Ukrainian PHP comunity
Ukrainian PHP comunity
885 учасників

dev-ua/php