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.