Einführung in die Zeichencodierung

UTF-16

UTF-16 ist ein 16-Bit-Codierungsschema mit variabler Länge und verwendet den UTF-Zeichensatz für Zeichencodepunkte. Dies bedeutet, dass ein UTF-16-codiertes Zeichen eine 16-Bit-Codeeinheit hat.

Da wir wissen, dass ein UTF-8-codiertes Zeichen in 1 bis 4 Codeeinheiten dargestellt werden kann, kann ein UTF-16-Zeichen in 1 oder 2 Codeeinheiten dargestellt werden. Daher kann ein UTF-16-Zeichen basierend auf seinem Codepunkt 16 oder 32 Bit Speicher belegen.

Bevor wir uns mit den UTF-16-Codierungsspezifikationen befassen, sollten wir verstehen, wie UTF-16 funktioniert.

Da wir eine 16-Bit-Codeeinheit haben, können wir theoretisch 21⁶ Zeichen vom Codepunkt 0 bis 65.535 codieren. Aber was ist, wenn wir ein Zeichen mit dem Codepunkt größer als 65.535 haben? In diesem Fall können wir eine weitere Codeeinheit hinzufügen.

Mit der zusätzlichen Codeeinheit können wir insgesamt 232 Zeichen codieren, was mehr als 4M ist. Aber dann ist die Frage, wie ein UTF-16-Decoder wissen wird, dass er 2 Codeeinheiten berücksichtigen muss, um ein Zeichen zu decodieren?UTF-8 löste dieses Problem, indem es Anfangsbits der ersten Codeeinheit und Fortsetzungscodeeinheiten auf bestimmte Bitwerte setzte, mit denen ein UTF-8-Decoder ableiten kann, wie viele Codeeinheiten ein Zeichen annehmen kann.

Wir können dasselbe mit der UTF-16-Codeeinheit tun, aber dann müssen wir einige Bits in einer Codeeinheit für diese Funktion opfern. Wir können einige Anfangsbits einer Codeeinheit auf einen aussagekräftigen Wert setzen, den ein UTF-16-Decoder verstehen kann.

Um Codeeinheiten selbstsynchronisierende Leistung zu verleihen, muss eine Codeeinheit in der Lage sein zu erkennen, ob es sich um die Anfangscodeeinheit oder eine Fortsetzungscodeeinheit handelt und nicht um ein Zeichen nur einer Codeeinheit.

Also entschied sich Unicode, die anfänglichen 6 Bits der Codeeinheit zu opfern, so dass nur 10 Bits übrig blieben, um den Codepunkt eines Zeichens pro Codeeinheit zu codieren. Wenn ein Zeichen 2 Codeeinheiten benötigt, enthalten 20 Bits des Speichers (von 32 Bits oder 4 Bytes) die tatsächlichen Codepunktinformationen des Zeichens.

Also, was sind diese anfänglichen Bits und wie machen diese Bits eine Delle im UTF-Zeichensatz? Folgen wir dem folgenden Beispiel.

1101 10xx xxxx xxxx 1101 11xx xxxx xxxx
FIRST CODE UNIT---- SECOND CODE UNIT---

Nach dem UTF-16-Standard sollte die erste Codeeinheit mit 110110₂ und die zweite Codeeinheit mit 110111₂ beginnen. Dies hilft einem UTF-16-Decoder zu verstehen, welcher die erste und welcher die zweite Codeeinheit ist. Dies macht UTF-16 selbstsynchronisierend.

Nun, was wir haben 10 Bits pro Codeeinheit zu spielen, was ist der Bereich, in dem wir spielen können? Wie viele Zeichen können am Ende in zwei Codeeinheiten der UTF-16-Codierung codiert werden?

Keine Sorge, wir werden über Zeichen sprechen, die in nur einer Codeeinheit codiert sind.

Wenn Sie sich die obigen Code-Unit-Vorlagen ansehen, haben wir einen Bereich von 1101 1000 0000 0000₂ bis 1101 1111 1111 1111₂. Das ist äquivalent zu d800₁₆ zu DFFF₁₆.

💡 Die erste Codeeinheit hat den Bereich von d800₁₆ bis 6fff₁₆ und die zweite Codeeinheit hat den Bereich von dc00₁₆ bis dfff₁₆. Wir können diese Werte erhalten, indem wir alle Codepunktbits ein- und ausschalten.

Da UTF-16 sich selbst synchronisieren muss, dürfen Codepunkte zwischen d800₁₆ und dfff₁₆ kein Zeichen in UTF-16 darstellen. Da alle UTF-Codierungen demselben UTF-Zeichensatz folgen, sind diese Codepunkte durch UTF eingeschränkt und werden keinem Zeichen zugewiesen⁰ .

Codepunkte zwischen d800₁₆ und dfff₁₆ stellen keine Zeichen dar, daher werden sie als Surrogat-Codepunkte oder zusammen auch als Surrogat-Paare bezeichnet⁰.

Der erste Surrogat-Codepunkt (von der ersten Codeeinheit) wird auch als hoher Surrogat und der zweite Codepunkt (von der zweiten Codeeinheit) auch als niedriger Surrogat bezeichnet. Insgesamt 2048 Codepunkte, 1024 pro Ersatz.

💁♂ Ersatzcodepunkte opferten ihr Leben, damit wir mehr Zeichen mit zwei Codeeinheiten codieren konnten. Denk darüber nach!

Also die große Frage, können wir ein Zeichen mit einer einzigen Codeeinheit von UTF-16 codieren? Die Antwort ist JA. UTF-16 ist ein 16-Bit-Kodierungsschema variabler Länge. Bedeutet das, dass wir 21⁶ Zeichen mit einer einzigen Codeeinheit codieren können?

Die Antwort lautet NEIN. In der Theorie könnten wir Kodieren 21⁶ Zeichen mit der code-Punkt 0000₁₆ (0₁₀) zu FFFF₁₆ (65535₁₀), aber code-Punkte zwischen D800₁₆ und DFFF₁₆ stellen keine Zeichen sind vorbehalten.

Daher ist es sicher, Zeichen von 0000₁₆ bis d7ff₁₆ und e000₁₆ bis ffff₁₆ zu codieren, was 63.488 (65536-2048) Zeichen entspricht. Dies gilt nur für die Zeichen, die in nur einer Codeeinheit von UTF-16 codiert werden können.

Da wir insgesamt 20 Bits haben, mit denen wir spielen können, wenn es um Zeichen geht, die in 2 Codeeinheiten von UTF-16 codiert werden können, können wir 22⁰ mehr Zeichen codieren, was 1.048.576 Zeichen entspricht.Insgesamt können wir also 1.048.576 + 63.488 codieren, was 1.112.064 Zeichen entspricht (mehr als 1 Million Zeichen). Dies ist die Grenze des UTF-Zeichensatzes. Da UTF-16 diese vielen Zeichen codieren kann, können andere UTF-Codierungen keine Zeichen darüber hinaus darstellen.

UTF-Zeichensatz-Codepunkte

Da wir wissen, dass ein Codepunkt ein Dezimalwert ist, der einem Zeichen zugewiesen ist, dürfen wir (Unicode) einem tatsächlichen Zeichen keinen ungültigen Codepunkt zuweisen. Bisher sind die ungültigen Codepunkte Ersatzcodepunkte.

Mit nur einer einzigen UTF-16-Codeeinheit können wir 63.488 Zeichen von 0000₁₆ bis d7ff₁₆ und e000₁₆ bis ffff₁₆ codieren. Der letzte Codepunkt ist 65.535. Diese werden BMP-Zeichen genannt (später erklärt).

Mit zwei Codeeinheiten von UTF-16 können wir 1.048.576 Zeichen codieren. Da wir nicht wieder vom 0-Wert (Codepunkt) ausgehen können, weil diese nach BMP-Zeichen kommen, müssen wir sie um 65.536 versetzen. Diese Zeichen werden als Zusatzzeichen bezeichnet (später erläutert).

Daher hat das erste Zusatzzeichen einen Codepunktwert von 65536₁₀, was 10000₁₆ entspricht. Da wir 1.048.576 Zeichen mit zwei Codeeinheiten von UTF-16 codieren können, ist der letzte Codepunkt 1114111₁₀, was 10ffff₁₆ entspricht.

Also lasst uns die Dinge in einer einfachen tabellarischen Form aufschlüsseln.

+-----------+---------------------+--------------------+
| UTF-16 CU | Code Point | |
+-----------+---------------------+--------------------+
| 1 | 0000₁₆ - D7FF₁₆ | valid |
+-----------+---------------------+--------------------+
| 1 | D800₁₆ - DFFF₁₆ | invalid(surrogate) |
+-----------+---------------------+--------------------+
| 1 | E000₁₆ - FFFF₁₆ | valid |
+-----------+---------------------+--------------------+
| 2 | 10000₁₆ - 10FFFF₁₆ | valid |
+-----------+---------------------+--------------------+
| | 110000₁₆ - FFFFFF₁₆ | unassigned |
+-----------+---------------------+--------------------+

Mit diesem Wissen wollen wir sehen, wie wir einige Zeichen in UTF-16 codieren können. Wählen wir ein einfaches ASCII-Zeichen A (Codepunkt: 41₁₆), ein Zeichen aus der Hindi (indischen) Sprache आ (ausgesprochen als Aa, Codepunkt: 906₁₆) und ein Emoticon 😊 (genannt als glückliches Gesicht, Codepunkt: 1f60a₁₆).

Wie wir aus der obigen Tabelle sehen können, können sowohl A als auch आ in nur einer Codeeinheit von UTF-16 codiert werden, da ihre Werte kleiner als ffff₁₆ sind.

Wenn wir ein Zeichen in nur einer Codeeinheit codieren müssen, müssen wir nur den Codepunkt des Zeichens in eine 16-Bit-Binärzahl konvertieren. Für die Zeichen A ist 00000000 010000010 die UTF-16-Darstellung.

In ähnlicher Weise müssen wir für das Zeichen आ nur seinen Codepunkt 906₁₆ in eine 16-Bit-Binärzahl konvertieren, die 00001001 000001101 ist.

Normalerweise stellen wir Codeeinheiten eines Zeichens in Hexadezimalzahlen dar. Daher ist für das Zeichen A die UTF-16-Darstellung 0041₁₆ und für das Zeichen die UTF-16-Darstellung आ 0906₁₆.

Für das Zeichen 😊 sieht es etwas anders aus. Sein Codepunkt ist 1f60a₁₆. Wenn wir uns die oben erwähnte UTF-16-Tabelle ansehen, muss sie in 2 Codeeinheiten von UTF-16 codiert sein. Also, wie fangen wir an?

Zuerst müssen wir 10000₁₆ vom Codepunkt subtrahieren. Der Grund dafür ist, dass jedes Zeichen, das in 2 Codeeinheiten von UTF-16 codiert ist, nach den BMP-Zeichen steht, deren letzter Codepunkt ffff₁₆ .

Um den tatsächlichen Wert der für die Codierung verwendeten Bits (20 in 2 Codeeinheiten) zu erhalten, müssen wir 10000₁₆ vom Codepunkt subtrahieren und die endgültige Zahl verwenden, um diese 20 Bits zu generieren.

💡 Zum besseren Verständnis werden beim ersten Zeichen, das mit 2 Codeeinheiten von UTF-16 dargestellt wird, alle 20 Bits auf 0 gesetzt. Der Wert der Bits, die zum Codieren des Codepunkts dieses Zeichens verwendet werden, ist also 0. Sein Codepunkt ist jedoch 10000₁₆ gemäß dem Unicode-Zeichensatz. Dies liegt daran, dass der Wert, der sich aus diesen 20 Bits ergibt, zu 10000₁₆ addiert wird, um den endgültigen Codepunkt zu generieren.

Wie bereits erwähnt, sehen diese 2 Codeeinheiten wie folgt aus.

1101 10xx xxxx xxxx 1101 11xx xxxx xxxx
FIRST CODE UNIT---- SECOND CODE UNIT---

Wir müssen nur diese 20 Bits (x) mit dem vom Zeichencodepunkt empfangenen Wert füllen. Der Codepunkt des Zeichens 😊 ist 1f60a₁₆. Aber zuerst müssen wir 10000₁₆ davon abziehen. Wir bekommen f60a₁₆.

Jetzt müssen wir f60a₁₆ in eine 20-Bit-Binärzahl konvertieren und die 20 Bits in der obigen Codeeinheitsvorlage füllen. f60a₁₆ in binär ist 0000111101 10000010101. Jetzt können wir diese 20 Platzhalterbits füllen.

Unten sind die endgültigen Codeeinheiten.

1101 1000 0011 1101 1101 1110 0000 1010
0xD83D 0xDE0A

Um schnell zu überprüfen, ob diese Codeeinheiten gültig sind und ob diese Ersatzpaare das codierte Zeichen darstellen können, öffnen Sie einen Browser DevTool und geben Sie console.log('\uD83D\uDE0A'); in der Konsole ein.

(Chrome-Entwicklertools)

Mit diesem Online-Tool können Sie auch UTF-16-Codepunkte generieren.

Unicode-Zeichenebenen

Eine Ebene ist eine kontinuierliche Gruppe von 21⁶ oder 65.536 Codepunkten. Da UTF-16 Codepunkte auf maximal 10ffff₁₆ beschränkt hat, haben wir im Unicode-Standard insgesamt 17 Zeichen, die von 0 bis 16 beginnen.

Da 21⁶ Zeichen durch die einzelne Codeeinheit von UTF-16 (einschließlich Ersatzcodepunkten) definiert werden können, bildet sie die erste (0.) Ebene. Dieses Flugzeug enthält fast alle Zeichen in Grundsprachen auf der ganzen Welt. Aus diesem Grund wird diese Ebene als Basic Multilingual Plane oder BMP bezeichnet.

Als nächstes haben wir Codepunkte, die durch zwei Codeeinheiten von UTF-16 definiert sind. Diese enthalten 22⁰ Zeichen, da wir 20 Bits haben, um den Codepunktwert zu codieren. Diese sind in 16 Ebenen (2⁴ x 21⁶) unterteilt. Diese werden Ergänzungsebenen genannt.

💡 Weitere Informationen zu diesen Flugzeugen finden Sie in diesem Wikipedia-Dokument.

Vergleich mit UCS-2

UCS-2 ist eine 16-Bit-Kodierung mit fester Breite. Das bedeutet, dass nur eine 16-Bit-Codeeinheit verwendet wird, um einen Codepunkt darzustellen. Theoretisch kann UCS-2 21 distinct verschiedene Zeichen darstellen, aber es gibt eine Wendung.

💡 Übrigens verwenden wir den Begriff Code unit in dieser Codierung mit fester Breite, um die Beziehung zwischen UTF-16 zu verstehen. In Wirklichkeit gibt es keine Codeeinheit in irgendeiner Form mit Codierung.

Da UCS dem Unicode-Zeichensatz folgt, ist die Codierung der Zeichen in UCS-2 identisch mit der Codierung der Zeichen in UTF-16, die in nur einer Codeeinheit dargestellt werden.

💡 Da UCS dem Unicode-Zeichensatz folgt, kann es kein gültiges Zeichen mit den für die Surrogate reservierten Codepunkten codieren.

Kurz gesagt, UCS-2 enthält die Zeichen von Basic Multilingual Plane. Dies ist der Grund, einige ältere Dokumente und Software verwendet UCS-2-Codierung. Die UCS-2-Codierung ist jedoch veraltet und UTF-16 wird bevorzugt.

Endianness und BOM

Wie bereits erwähnt, enthalten UTF-codierte Dokumente auf niedriger Ebene die Folge von Codeeinheiten. Für UTF-8 ist die Codeeinheit 8 Bit lang, während sie für UTF-16 16 Bit lang ist. Diese Codeeinheiten bilden die Zeichen.

Ein UTF-8- oder UTF-16-Decoder liest die Codeeinheiten nacheinander, jeweils eine Codeeinheit, um die Zeichen zu generieren.

Jede Codeeinheit stellt einen numerischen Wert dar, den ein UTF-8- oder UTF-16-Decoder betrachten und entscheiden kann, ob er ausreicht, um ein Zeichen darzustellen, oder ob er anderen Codeeinheiten folgt, die ebenfalls berücksichtigt werden sollten.

Wenn es um UTF-8 geht, sind die Dinge einfach. Da jede Codeeinheit 8 Bit lang ist, ist die Konvertierung dieser 8-Bit-Binärzahl in einen numerischen Wert schnell und einfach. Dies ist jedoch bei UTF-16 nicht der Fall.

UTF-16 Code unit ist eine 16-Bit (2 Byte) Binärzahl, die einen Codepunktwert darstellt. Den numerischen Wert aus mehreren Bytes zu generieren, ist im Allgemeinen schwierig und verschiedene Systeme verhalten sich unterschiedlich.

Dieses Verhalten hängt von der Endianness des Systems ab. Aus unserer früheren Diskussion über Endianness gibt es zwei Möglichkeiten, einen UTF-16-Codeeinheitswert zu schreiben. Entweder im Big-Endian-Format oder im Little-Endian-Format.

Im Big-Endian-Format wird zuerst der MSB und zuletzt der LSB gespeichert. Bisher schreiben wir den UTF-16-Codeeinheitswert im Big-Endian-Format. Um den UTF-16-Codeeinheitswert in Little-Endian zu schreiben, müssen wir Bytes austauschen.

Lassen Sie uns über das Zeichen आ sprechen. Aus dem früheren Beispiel kann es in nur einer Codeeinheit von UTF-16 dargestellt werden, und seine Codierung in hexadezimaler Darstellung sieht wie 0906₁₆ aus.

0906₁₆ ist eine 16-Bit-Zahl, wobei 09 der MSB und 06 der LSB ist. Daher wird es in der Big-Endian-Architektur als 09 06 gespeichert. In einer Little-Endian-Architektur wird es jedoch als 06 09 gespeichert.

Daher liegt es in unserer Verantwortung, Zeichen zu codieren, indem wir die Endianness des Systems berücksichtigen, damit das System ein UTF-16-Dokument korrekt lesen kann.

Aber wie können wir vorher feststellen, ob der Computer eines Benutzers mit dem codierten Dokument kompatibel ist oder nicht? Und da die Endianness eines Systems beeinflussen kann, wie ein Dokument decodiert wird, wie teilen wir es öffentlich?

Hier kommt BOM ins Spiel. Eine Byte Order Mark (BOM) ist eine Byte-Sequenz, die am Anfang einer Textdatei oder Textdaten hinzugefügt wird.

Unicode empfiehlt Zeichen mit dem Codepunkt feff₁₆ als Stückliste für UTF-16- und UTF-32-Codierungen. Dieses Zeichen sollte vor dem ersten Zeichen des Dokuments stehen. Dieses Zeichen wird jedoch vom Decoder in der Ausgabe nicht berücksichtigt.

Dieses Zeichen (U+FEFF) ist ein Non-Breaking Space-Zeichen (ZWNBSP) mit einer Breite von Null und ist unsichtbar. Selbst wenn ein Decoder die Stückliste nicht erkennt, erzeugt er keine sichtbare Ausgabe.

Dieses Zeichen wird in einer einzigen Codeeinheit von UTF-16 dargestellt und sieht in hexadezimaler Darstellung wie FE (MSB) und FF (LSB) aus.

Wenn also Zeichen im Big-Endian-Format codiert sind, müssen wir FEFF als Stückliste am Anfang der Datei hinzufügen, und wenn Zeichen im Little-Endian-Format codiert sind, müssen wir FFFE (reverse) als Stückliste am Anfang der Datei hinzufügen.

Unicode empfiehlt, die Stückliste zu einem UTF-16-codierten Dokument hinzuzufügen. Wenn die Stückliste jedoch fehlt, wird das Big-Endian-Format angenommen.

IANA bevorzugt UTF-16 als Bezeichner, um ein UTF-16-codiertes Dokument anzuzeigen. UTF-16BE wird jedoch für Dokumente verwendet, die im Big-Endian-Format codiert sind, und UTF-16LE wird für das Little-Endian-Format verwendet.

Wenn der Name UTF-16BE oder UTF-16LE verwendet wird, wird nicht empfohlen, BOM einer Datei voranzustellen. Selbst in diesem Fall wird die Stückliste, wenn sie hinzugefügt wird, als ZWNBSP-Zeichen betrachtet und nicht ignoriert.

💡 UTF-16-, UTF-16BE- und UTF-16LE-Namen unterscheiden nicht zwischen Groß- und Kleinschreibung.

Vor- und Nachteile

UTF-16 ist effizient, da es nur 2 Codeeinheiten hat und da die meisten verwendeten Zeichen in den BMP-Satz fallen, können sie in nur einer Codeeinheit dargestellt werden. Es kommt jedoch mit vielen Problemen.

Der größte Nachteil von UTF-16 ist, dass es nicht ASCII-kompatibel ist. Da ASCII-Zeichen mit einer einzigen Codeeinheit (16-Bit-Zahl) codiert werden, können sie von einem ASCII-Decoder nicht richtig decodiert werden.

UTF-16 verbraucht unnötigen Speicherplatz für ASCII-Zeichen. Im Vergleich zum UTF-8-codierten Dokument, das nur ASCII-Zeichen enthält, ist die Größe desselben in UTF-16 codierten Dokuments doppelt so groß.

UTF-16 wird auch von der Endianness des Systems beeinflusst. Wenn die Stückliste fehlt und keine geeignete Codierungskennung verwendet wird (z. B. UTF-16LE), wird ein UTF-16-codiertes Dokument möglicherweise nicht ordnungsgemäß decodiert.

Aufgrund der Art der UTF-16-Codierung wurden Ersatzcodepunkte eingeführt, die die gültigen Zeichen nicht darstellen können. Außerdem wurde der Unicode-Zeichensatz auf 10ffff₁₆ (letzter Codepunkt) beschränkt.

Trotz dieser Tatsachen, einige der Programmiersprachen wie JavaScript, Java, etc. und Systeme wie Windows bevorzugen UTF-16-Codierung.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.