Worum geht es bei normalisiertem UTF-8?


129

Das ICU-Projekt (das jetzt auch eine PHP-Bibliothek enthält) enthält die Klassen, die zur Normalisierung von UTF-8-Zeichenfolgen erforderlich sind, um den Vergleich von Werten bei der Suche zu erleichtern.

Ich versuche jedoch herauszufinden, was dies für Anwendungen bedeutet . In welchen Fällen möchte ich beispielsweise "Kanonische Äquivalenz" anstelle von "Kompatibilitätsäquivalenz" oder umgekehrt?


229
Wer ̸͢k̵͟n̴͘ǫw̸̛s͘ w͘͢ḩ̵a҉̡͢t Horrors LIE im dunklen Herzen UNICODE ͞
ObscureRobot

@ObscureRobot Ich möchte wirklich wissen, ob diese zusätzlichen Symbole Zustände haben können oder nicht
eonil

1
@Eonil - Ich bin mir nicht sicher, was Status im Kontext von Unicode bedeutet.
ObscureRobot

@ObscureRobot Zum Beispiel ein Codepunkt wie dieser: (begin curved line) (char1) (char2) … (charN) (end curved line)anstatt dieser : (curved line marker prefix) (char1) (curved line marker prefix) (char2) (curved line marker prefix) (char2). Mit anderen Worten, minimale Einheit, die gerendert werden kann?
Eonil

2
Das klingt nach einer guten Frage für sich.
ObscureRobot

Antworten:


181

Alles, was Sie nie über Unicode-Normalisierung wissen wollten

Kanonische Normalisierung

Unicode bietet mehrere Möglichkeiten zum Codieren einiger Zeichen, insbesondere von Zeichen mit Akzent. Die kanonische Normalisierung ändert die Codepunkte in eine kanonische Codierungsform. Die resultierenden Codepunkte sollten mit den ursprünglichen identisch sein, mit Ausnahme von Fehlern in den Schriftarten oder der Rendering-Engine.

Wann zu verwenden

Da die Ergebnisse identisch erscheinen, ist es immer sicher, eine kanonische Normalisierung auf eine Zeichenfolge anzuwenden, bevor diese gespeichert oder angezeigt wird, solange Sie tolerieren können, dass das Ergebnis nicht Bit für Bit mit der Eingabe identisch ist.

Die kanonische Normalisierung erfolgt in zwei Formen: NFD und NFC. Die beiden sind in dem Sinne äquivalent, dass man ohne Verlust zwischen diesen beiden Formen konvertieren kann. Der Vergleich zweier Zeichenfolgen unter NFC führt immer zum gleichen Ergebnis wie der Vergleich unter NFD.

NFD

NFD hat die Zeichen vollständig erweitert. Dies ist die schnellere zu berechnende Normalisierungsform, die jedoch zu mehr Codepunkten führt (dh mehr Platz benötigt).

Wenn Sie nur zwei Zeichenfolgen vergleichen möchten, die noch nicht normalisiert sind, ist dies die bevorzugte Normalisierungsform, es sei denn, Sie wissen, dass Sie eine Kompatibilitätsnormalisierung benötigen.

NFC

NFC kombiniert Codepunkte nach Möglichkeit neu, nachdem der NFD-Algorithmus ausgeführt wurde. Dies dauert etwas länger, führt jedoch zu kürzeren Saiten.

Kompatibilitätsnormalisierung

Unicode enthält auch viele Zeichen, die wirklich nicht dazu gehören, aber in älteren Zeichensätzen verwendet wurden. Unicode fügte diese hinzu, damit Text in diesen Zeichensätzen als Unicode verarbeitet und dann ohne Verlust zurückkonvertiert werden kann.

Die Kompatibilitätsnormalisierung konvertiert diese in die entsprechende Folge von "echten" Zeichen und führt auch eine kanonische Normalisierung durch. Die Ergebnisse der Kompatibilitätsnormalisierung sind möglicherweise nicht mit den Originalen identisch.

Zeichen, die Formatierungsinformationen enthalten, werden durch Zeichen ersetzt, die dies nicht tun. Zum Beispiel wird der Charakter in konvertiert 9. Andere beinhalten keine Formatierungsunterschiede. Beispielsweise wird das römische Ziffernzeichen in reguläre Buchstaben umgewandelt IX.

Sobald diese Transformation durchgeführt wurde, ist es offensichtlich nicht mehr möglich, verlustfrei zum ursprünglichen Zeichensatz zurückzukehren.

Wann zu verwenden

Das Unicode-Konsortium schlägt vor, die Kompatibilitätsnormalisierung wie eine ToUpperCaseTransformation zu betrachten. Es ist etwas, das unter bestimmten Umständen nützlich sein kann, aber Sie sollten es nicht einfach so oder so anwenden.

Ein ausgezeichneter Anwendungsfall wäre eine Suchmaschine, da Sie wahrscheinlich eine 9passende Suche wünschen würden .

Eine Sache, die Sie wahrscheinlich nicht tun sollten, ist das Ergebnis der Anwendung der Kompatibilitätsnormalisierung auf den Benutzer anzuzeigen.

NFKC / NFKD

Das Kompatibilitätsnormalisierungsformular gibt es in zwei Formen: NFKD und NFKC. Sie haben die gleiche Beziehung wie zwischen NFD und C.

Jede Zeichenfolge in NFKC ist von Natur aus auch in NFC enthalten, und dies gilt auch für NFKD und NFD. Also NFKD(x)=NFD(NFKC(x))und NFKC(x)=NFC(NFKD(x))usw.

Fazit

Wenn Sie Zweifel haben, fahren Sie mit der kanonischen Normalisierung fort. Wählen Sie NFC oder NFD basierend auf dem anwendbaren Kompromiss zwischen Platz und Geschwindigkeit oder basierend auf den Anforderungen, die für etwas erforderlich sind, mit dem Sie zusammenarbeiten.


42
Eine Kurzreferenz, um sich daran zu erinnern, wofür die Abkürzungen stehen: NF = normalisierte Form D = Zerlegen (Dekomprimieren) , C = Zusammensetzen (Komprimieren) K = Kompatibilität (da "C" genommen wurde).
Mike Spross

12
Sie möchten immer alle Zeichenfolgen bei der Eingabe als Ganzes NFD und alle Zeichenfolgen bei der Eingabe als Ganzes NFC. Das ist bekannt.
Tchrist

3
@tchrist: Dies ist im Allgemeinen ein guter Rat, außer in den seltenen Fällen, in denen die Ausgabe Byte für Byte mit der Eingabe identisch sein soll, wenn keine Änderungen vorgenommen werden. Es gibt einige andere Fälle, in denen Sie NFC im Speicher oder NFD auf der Festplatte möchten, aber dies ist eher die Ausnahme als die Regel.
Kevin Cathcart

@ Kevin: Ja, NFD rein und NFC raus zerstören die Singletons. Ich bin mir nicht sicher, ob sich jemand darum kümmert, aber möglicherweise.
Tchrist

2
Sie könnten das denken, aber aus dem Anhang: "Um eine Unicode-Zeichenfolge in eine bestimmte Unicode-Normalisierungsform umzuwandeln, besteht der erste Schritt darin, die Zeichenfolge vollständig zu zerlegen." Selbst wenn NFC ausgeführt wird, wird Q-Caron zunächst zu Q + Caron und kann nicht neu zusammengesetzt werden, da die Stabilitätsregeln das Hinzufügen der neuen Zusammensetzungszuordnung verbieten. NFC ist effektiv definiert als NFC(x)=Recompose(NFD(x)).
Kevin Cathcart

40

Einige Zeichen, z. B. ein Buchstabe mit einem Akzent (z. B. é), können auf zwei Arten dargestellt werden - als einzelner Codepunkt U+00E9oder als einfacher Buchstabe, gefolgt von einer kombinierten Akzentmarke U+0065 U+0301. Bei der normalen Normalisierung wird eine davon ausgewählt, um sie immer darzustellen (der einzelne Codepunkt für NFC, die Kombinationsform für NFD).

Für Zeichen, die durch mehrere Sequenzen von Basiszeichen und Kombinationsmarkierungen dargestellt werden können (z. B. "s, Punkt unten, Punkt oben" oder Punkt oben und Punkt unten setzen oder ein Basiszeichen verwenden, das bereits einen der Punkte enthält), wird NFD dies tun Wählen Sie auch eine davon aus (siehe unten zuerst, wie es passiert)

Die Kompatibilitätszerlegungen enthalten eine Reihe von Zeichen, die eigentlich keine Zeichen sein sollten, sondern darauf zurückzuführen sind, dass sie in Legacy-Codierungen verwendet wurden. Durch die normale Normalisierung werden diese nicht vereinheitlicht (um die Integrität des Roundtrips zu gewährleisten - dies ist kein Problem für das Kombinieren von Formularen, da keine Legacy-Codierung (außer einer Handvoll vietnamesischer Codierungen) beide verwendet), die Kompatibilitätsnormalisierung jedoch. Denken Sie an das "kg" -Kilogrammzeichen, das in einigen ostasiatischen Codierungen (oder an Katakana und Alphabet mit halber / voller Breite) oder an die "fi" -Ligatur in MacRoman vorkommt.

Weitere Informationen finden Sie unter http://unicode.org/reports/tr15/ .


1
Dies ist in der Tat die richtige Antwort. Wenn Sie nur kanonische Normalisierung für Text verwenden, der aus einem älteren Zeichensatz stammt, kann das Ergebnis ohne Verlust wieder in diesen Zeichensatz konvertiert werden. Wenn Sie die Kompatibilitätszerlegung verwenden, werden keine Kompatibilitätszeichen mehr angezeigt. Es ist jedoch nicht mehr möglich, ohne Verlust zum ursprünglichen Zeichensatz zurückzukehren.
Kevin Cathcart

13

Normale Formen (von Unicode, keine Datenbanken) befassen sich hauptsächlich (ausschließlich?) Mit Zeichen, die diakritische Zeichen aufweisen. Unicode bietet einigen Zeichen "eingebaute" diakritische Zeichen, wie z. B. U + 00C0, "Latin Capital A with Grave". Das gleiche Zeichen kann aus einer "lateinischen Hauptstadt A" (U + 0041) mit einem "Combining Grave Accent" (U + 0300) erstellt werden. Dies bedeutet, dass die beiden Sequenzen das gleiche resultierende Zeichen erzeugen, ein Byte für Byte Ein Vergleich zeigt, dass sie völlig anders sind.

Normalisierung ist ein Versuch, damit umzugehen. Durch das Normalisieren wird sichergestellt (oder zumindest versucht), dass alle Zeichen auf dieselbe Weise codiert werden - entweder alle mit einem separaten kombinierten diakritischen Zeichen, falls erforderlich, oder alle, wo immer möglich, mit einem einzigen Codepunkt. Unter dem Gesichtspunkt des Vergleichs spielt es keine Rolle, welche Menge Sie wählen - so ziemlich jede normalisierte Zeichenfolge wird richtig mit einer anderen normalisierten Zeichenfolge verglichen.

In diesem Fall bedeutet "Kompatibilität" Kompatibilität mit Code, bei dem davon ausgegangen wird, dass ein Codepunkt einem Zeichen entspricht. Wenn Sie solchen Code haben, möchten Sie wahrscheinlich das normale Kompatibilitätsformular verwenden. Obwohl ich es noch nie direkt gesehen habe, implizieren die Namen der normalen Formen, dass das Unicode-Konsortium es für vorzuziehen hält, separate kombinierte diakritische Zeichen zu verwenden. Dies erfordert mehr Intelligenz, um die tatsächlichen Zeichen in einer Zeichenfolge zu zählen (sowie Dinge wie das intelligente Brechen einer Zeichenfolge), ist jedoch vielseitiger.

Wenn Sie die Intensivstation voll ausnutzen, besteht die Möglichkeit, dass Sie die kanonische Normalform verwenden möchten. Wenn Sie versuchen, selbst Code zu schreiben, der (zum Beispiel) davon ausgeht, dass ein Codepunkt einem Zeichen entspricht, möchten Sie wahrscheinlich die normale Kompatibilitätsform, die dies so oft wie möglich wahr macht.


Dies ist also der Teil, in dem die Graphemfunktionen dann eingehen . Das Zeichen ist nicht nur mehr Bytes als ASCII - sondern mehrere Sequenzen können ein einziges Zeichen sein, oder? (Im Gegensatz zu den MB-String- Funktionen.)
Xeoncross

4
Nein, der 'Ein Codepunkt ist ein Zeichen' entspricht in etwa NFC (der mit den Kombinationsmarkierungen ist NFD und keiner von beiden ist "Kompatibilität"). - Die Kompatibilitätsnormalisierungen NFKC / NFKD sind ein anderes Problem. Kompatibilität (oder deren Fehlen) für Legacy-Codierungen, die z. B. separate Zeichen für Griechisch mu und 'Mikro' hatten (das macht Spaß, weil die "Kompatibilitäts" -Version im lateinischen 1-Block enthalten ist)
Random832

@ Random832: Ups, ganz richtig. Ich sollte es besser wissen, als aus dem Gedächtnis zu verschwinden, wenn ich in den letzten ein oder zwei Jahren nicht damit gearbeitet habe.
Jerry Coffin

@ Random832 Das stimmt nicht. Dein "grob" ist zu da draußen. Betrachten Sie die beiden Grapheme ō̲̃ und ȭ̲. Es gibt viele verschiedene Möglichkeiten, diese zu schreiben, von denen jeweils genau eine NFC und eine NFD ist, aber es gibt auch andere. Es ist kein Fall, dass nur ein Codepunkt. NFD für das erste ist "o\x{332}\x{303}\x{304}"und NFC ist "\x{22D}\x{332}". Für die zweite ist NFD "o\x{332}\x{304}\x{303}"und NFC ist "\x{14D}\x{332}\x{303}". Es gibt jedoch viele nicht-kanonische Möglichkeiten, die diesen kanonisch äquivalent sind. Die Normalisierung ermöglicht den binären Vergleich kanonisch äquivalenter Grapheme.
Tchrist

5

Wenn zwei Unicode-Zeichenfolgen kanonisch äquivalent sind, sind die Zeichenfolgen tatsächlich gleich und verwenden nur unterschiedliche Unicode-Sequenzen. Zum Beispiel kann Ä entweder mit dem Zeichen Ä oder einer Kombination von A und ◌̈ dargestellt werden.

Wenn die Zeichenfolgen nur kompatibel sind, sind die Zeichenfolgen nicht unbedingt gleich, können jedoch in einigen Kontexten gleich sein. ZB könnte ff als ff angesehen werden.

Wenn Sie also Zeichenfolgen vergleichen, sollten Sie die kanonische Äquivalenz verwenden, da die Kompatibilitätsäquivalenz keine echte Äquivalenz ist.

Wenn Sie jedoch eine Reihe von Zeichenfolgen sortieren möchten, ist es möglicherweise sinnvoll, die Kompatibilitätsäquivalenz zu verwenden, da diese nahezu identisch sind.


5

Das ist eigentlich ziemlich einfach. UTF-8 hat tatsächlich mehrere verschiedene Darstellungen desselben "Charakters". (Ich verwende Zeichen in Anführungszeichen, da sie byteweise unterschiedlich sind, aber praktisch gleich). Ein Beispiel finden Sie im verknüpften Dokument.

Das Zeichen "Ç" kann als Bytefolge 0xc387 dargestellt werden. Es kann aber auch durch a C(0x43) gefolgt von der Bytefolge 0xcca7 dargestellt werden. Man kann also sagen, dass 0xc387 und 0x43cca7 dasselbe Zeichen sind. Der Grund, der funktioniert, ist, dass 0xcca7 eine Kombinationsmarke ist; das heißt, es nimmt den Charakter davor (a Chier) und modifiziert ihn.

Was nun den Unterschied zwischen kanonischer Äquivalenz und Kompatibilitätsäquivalenz betrifft, müssen wir uns die Zeichen im Allgemeinen ansehen.

Es gibt zwei Arten von Zeichen, diejenigen, die durch den Wert Bedeutung vermitteln , und diejenigen, die ein anderes Zeichen nehmen und es ändern. 9 ist ein bedeutungsvoller Charakter. Ein Superskript ⁹ nimmt diese Bedeutung an und ändert sie durch Präsentation. Kanonisch haben sie also unterschiedliche Bedeutungen, aber sie repräsentieren immer noch den Grundcharakter.

Bei der kanonischen Äquivalenz gibt die Byte-Sequenz dasselbe Zeichen mit derselben Bedeutung wieder. Kompatibilitätsäquivalenz ist, wenn die Byte-Sequenz ein anderes Zeichen mit derselben Basisbedeutung wiedergibt (obwohl es möglicherweise geändert wird). Die 9 und ⁹ sind Kompatibilitätsäquivalente, da beide "9" bedeuten, aber nicht kanonisch äquivalent, da sie nicht dieselbe Darstellung haben.


@tchrist: Lies die Antwort noch einmal. Ich habe noch nie die verschiedenen Möglichkeiten erwähnt, denselben Codepunkt darzustellen. Ich sagte, es gibt mehrere Möglichkeiten, dasselbe gedruckte Zeichen darzustellen (über Kombinatoren und mehrere Zeichen). Dies gilt sowohl für UTF-8 als auch für Unicode. Ihre Ablehnung und Ihr Kommentar gelten also überhaupt nicht für das, was ich gesagt habe. Tatsächlich habe ich im Grunde den gleichen Punkt gemacht, den das oberste Poster hier gemacht hat (wenn auch nicht so gut) ...
ircmaxell

4

Ob kanonische Äquivalenz oder Kompatibilitätsäquivalenz für Sie relevanter ist, hängt von Ihrer Anwendung ab. Die ASCII-Denkweise bei String-Vergleichen entspricht in etwa der kanonischen Äquivalenz, aber Unicode repräsentiert viele Sprachen. Ich glaube nicht, dass man davon ausgehen kann, dass Unicode alle Sprachen so codiert, dass Sie sie wie westeuropäisches ASCII behandeln können.

Die Abbildungen 1 und 2 zeigen gute Beispiele für die beiden Äquivalenzarten. Unter Kompatibilitätsäquivalenz sieht es so aus, als würde dieselbe Zahl in Unter- und Superskriptform gleich verglichen. Aber ich bin mir nicht sicher, ob das das gleiche Problem löst wie die kursive arabische Form oder die gedrehten Zeichen.

Die harte Wahrheit der Unicode-Textverarbeitung ist, dass Sie tief über die Textverarbeitungsanforderungen Ihrer Anwendung nachdenken und diese dann so gut wie möglich mit den verfügbaren Tools angehen müssen. Das geht nicht direkt auf Ihre Frage ein, aber eine detailliertere Antwort würde Sprachexperten für jede der Sprachen erfordern, die Sie unterstützen möchten.


1

Das Problem beim Vergleichen von Zeichenfolgen : Zwei Zeichenfolgen mit einem Inhalt, der für die meisten Anwendungen gleichwertig ist, können unterschiedliche Zeichenfolgen enthalten.

Siehe Unicodes kanonische Äquivalenz : Wenn der Vergleichsalgorithmus einfach ist (oder schnell sein muss), wird die Unicode-Äquivalenz nicht durchgeführt. Dieses Problem tritt beispielsweise beim kanonischen XML-Vergleich auf, siehe http://www.w3.org/TR/xml-c14n

Um dieses Problem zu vermeiden ... Welcher Standard ist zu verwenden? "erweitertes UTF8" oder "kompaktes UTF8"?
Verwenden Sie "ç" oder "c + ◌̧".

W3C und andere (z. B. Dateinamen ) schlagen vor, "komponiert als kanonisch" zu verwenden (beachten Sie C der "kompaktesten" kürzeren Zeichenfolgen) ... Also,

Der Standard ist C ! Verwenden Sie im Zweifelsfall NFC

Für die Interoperabilität und für die Auswahl "Konvention über Konfiguration" wird die Verwendung von NFC empfohlen , um externe Zeichenfolgen zu "kanonisieren". Um beispielsweise kanonisches XML zu speichern, speichern Sie es im "FORM_C". Die CSV in der Web-Arbeitsgruppe des W3C empfiehlt ebenfalls NFC (Abschnitt 7.2).

PS: de "FORM_C" ist das Standardformular in den meisten Bibliotheken. Ex. in PHPs normalizer.isnormalized () .


Der Begriff " Kompostierungsform " ( FORM_C) wird sowohl für "eine Zeichenfolge in der C-kanonischen Form" (das Ergebnis einer NFC-Transformation) als auch für die Verwendung eines Transformationsalgorithmus verwendet ... Siehe http: //www.macchiato.com/unicode/nfc-faq

(...) Jede der folgenden Sequenzen (die ersten beiden sind Einzelzeichenfolgen) repräsentiert dasselbe Zeichen:

  1. U + 00C5 (Å) LATEINISCHER GROSSBUCHSTABE A MIT RING OBEN
  2. U + 212B (Å) ANGSTROM-ZEICHEN
  3. U + 0041 (A) LATEINISCHER GROSSBUCHSTABE A + U + 030A (̊) KOMBINIERENDER RING OBEN

Diese Sequenzen werden als kanonisch äquivalent bezeichnet. Die erste dieser Formen heißt NFC - für Normalisierungsform C, wobei C für die Kompostierung steht . (...) Eine Funktion, die eine Zeichenfolge S in die NFC-Form umwandelt, kann als abgekürzt werden toNFC(S), während eine Funktion, die prüft, ob S in NFC vorliegt, als abgekürzt wird isNFC(S).


Hinweis: Zum Testen der Normalisierung kleiner Zeichenfolgen (reine UTF-8- oder XML-Entitätsreferenzen) können Sie diesen Test / Normalisierungs-Online-Konverter verwenden .


Ich bin verwirrt. Ich bin auf diese Online-Testerseite gegangen und gebe dort ein: "TÖST MÉ eappé". und versuchen Sie alle 4 gegebenen Normalisierungen - keine ändert meinen Text in irgendeiner Weise, außer dass er die Codes ändert, die zur Darstellung dieser Zeichen verwendet werden. Denke ich fälschlicherweise, dass "Normalisierung" bedeutet "alle diakritischen Zeichen und ähnliches entfernen", und es bedeutet tatsächlich - ändern Sie einfach die darunter liegende utf-Codierung?
Userfuser

Hallo @userfuser, vielleicht brauchen Sie eine Position zur Bewerbung: Ist es, Ihren Text zu vergleichen oder zu standardisieren ? In meinem Beitrag hier geht es nur darum, Anwendungen zu "standardisieren". PS: Wenn die ganze Welt Standard verwendet, verschwindet das Vergleichsproblem.
Peter Krauss
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.