Warum verschwendet UTF-8 mehrere Bits in seiner Codierung

16

Laut Wikipedia-Artikel hat UTF-8 das folgende Format:

Erster Code Letzter Code Bytes Byte 1 Byte 2 Byte 3 Byte 4
point point Verwendet
U + 0000 U + 007F 1 0xxxxxxx
U + 0080 U + 07FF 2 110xxxxx 10xxxxxx
U + 0800 U + FFFF 3 1110xxxx 10xxxxxx 10xxxxxx
U + 10000 U + 1FFFFF 4 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
x bedeutet, dass dieses Bit zur Auswahl des Codepunkts verwendet wird.

Dies verschwendet zwei Bits in jedem Fortsetzungsbyte und ein Bit im ersten Byte. Warum ist UTF-8 nicht wie folgt codiert?

Erster Code Letzter Code Bytes Byte 1 Byte 2 Byte 3
point point Verwendet
U + 0000 U + 007F 1 0xxxxxxx
U + 0080 U + 3FFF 2 10xxxxxx xxxxxxxx
U + 0800 U + 1FFFFF 3 110xxxxx xxxxxxxx xxxxxxxx

Es würde ein Byte sparen, wenn sich der Codepunkt außerhalb der mehrsprachigen Grundebene befindet oder wenn sich der Codepunkt im Bereich [U + 800, U + 3FFF] befindet.

Warum wird UTF-8 nicht effizienter codiert?

qbt937
quelle
3
cl.cam.ac.uk/~mgk25/ucs/utf-8-history.txt Ihre vorgeschlagene Codierung ähnelt dem ursprünglichen FSS / UTF-Vorschlag. Ken Thompson und Rob Pike wollten die selbstsynchronisierende Eigenschaft.
Ninjalj
4
Außerdem scheint Ihre Codierung nicht zu garantieren, dass ASCII-Codewerte in keinem Teil der Darstellung für Nicht-ASCII-Zeichen angezeigt werden. FSS / UTF und UTF-8 können mit älteren Programmen verwendet werden (z. B. mit ASCII NUL und Schrägstrich (Pfadtrennzeichen) als Trennzeichen).
Ninjalj

Antworten:

25

Dies geschieht, damit Sie erkennen können, wann Sie sich in der Mitte einer Multibyte-Sequenz befinden. Wenn Sie sich UTF-8-Daten ansehen 10xxxxxx, wissen Sie , dass Sie sich in der Mitte eines Multibyte-Zeichens befinden und im Stream sichern sollten, bis entweder 0xxxxxxoder angezeigt wird 11xxxxxx. Bei Verwendung Ihres Schemas könnten die Bytes 2 oder 3 leicht zu Mustern wie entweder 0xxxxxxxoder führen11xxxxxx

Denken Sie auch daran, dass die Menge der gespeicherten Daten von der Art der zu codierenden Zeichenfolge abhängt. Bei den meisten Texten, auch bei asiatischem Text, werden bei normalem Text selten oder nie vier Byte große Zeichen angezeigt. Auch naive Einschätzungen der Menschen darüber, wie Text aussehen wird, sind oft falsch. Ich habe Text für UTF-8 lokalisiert, der japanische, chinesische und koreanische Zeichenfolgen enthält, aber es ist eigentlich Russisch, das den größten Platz einnimmt. (Da in unseren asiatischen Zeichenfolgen häufig lateinische Schriftzeichen für Eigennamen, Interpunktion usw. verwendet werden und das durchschnittliche chinesische Wort 1-3 Zeichen lang ist, während das durchschnittliche russische Wort sehr viel länger ist.)

Gort den Roboter
quelle
Aber mit mir Schema, wenn Sie an einem Ort beginnen, von dem bekannt ist, dass er am Anfang eines Zeichens steht, können Sie erkennen, wie viele Bytes sich im Zeichen befinden, und zum Anfang des nächsten Zeichens gelangen.
qbt937
11
Sicher. Ihr Schema enthält mehr Informationen, hat jedoch keine wichtige Funktion, die UTF-8 bietet. Im Allgemeinen bevorzugen die Menschen die Sicherheit, weshalb UTF-8 möglich ist. Um zu beweisen, dass Ihr Schema tatsächlich effizienter ist, möchten Sie Statistiken mit echtem Text bereitstellen. Möglicherweise stellen Sie fest, dass in den meisten echten Texten Ihr Schema einen sehr geringen Betrag einspart und die Einsparungen sich daher nicht lohnen.
Gort the Robot
3
Ein weiteres wichtiges Merkmal: Wenn es keinen eingebetteten Null-Codepunkt gibt, enthält die Zeichenfolge keine eingebetteten Nullen.
Deduplizierer
Für thailändische Schrift müssen Sie 4 Bytes pro gedrucktem Zeichen zulassen. Sie kamen nicht nur zu spät zur Party und bekamen eine hoch nummerierte Codegruppe. Viele Dinge, die beim Drucken wie ein einzelnes Zeichen aussehen, bestehen tatsächlich aus drei verschiedenen Unicode-Zeichen.
James Anderson
@ qbt937: Wie kann man anhand Ihres Schemas schnell prüfen, ob eine Zeichenfolge eine andere enthält?
Supercat
6

Der offizielle Weg informiert den Decoder, wenn er sich in der Mitte des Tupels befindet, und er kann Bytes überspringen (oder rückwärts gehen), bis das Byte mit 0oder beginnt 11. Auf diese Weise wird verhindert, dass bei einer Beschädigung eines einzelnen Bytes Datenmüll anfällt.

Ratschenfreak
quelle
3

Kurze Antwort, Ihr Vorschlag unterscheidet nicht zwischen dem ersten Byte und dem Fortsetzungsbyte.

Das Bitmuster am oberen Ende des ersten Bytes gibt an, aus wie vielen Bytes das eigentliche Zeichen besteht. Diese Muster bieten auch eine gewisse Fehlererkennung beim Parsen einer Zeichenfolge. Wenn Sie das (scheinbar) erste Byte eines Zeichens lesen und 10xxxxxx erhalten, wissen Sie, dass Sie nicht synchron sind.

Kitana
quelle
2

Was nicht erwähnt wurde, ist, dass Sie mit UTF-8 den Zeiger auf das erste Byte sehr leicht finden können, wenn Sie eine korrekte Sequenz von Codepunkten und einen Zeiger haben, der garantiert auf das erste Byte zeigt des vorherigen Codepunkts (überspringen Sie alle Bytes, die mit 01xx xxxx beginnen). Mit Ihrer Codierung ist es unmöglich, ohne potenziell alle Bytes bis zum Anfang der Zeichenfolge zu untersuchen.

Betrachten Sie die Folgen von (2n + 2) Bytes

0xxxxxxx
n times (10xxxxxx, 10xxxxxx)
0xxxxxxx

und

n times (10xxxxxx, 10xxxxxx)
(10xxxxxx, 0xxxxxxx)

Wenn Sie nach dieser Sequenz einen Zeiger auf das erste Byte des ersten Codepunkts haben, müssen Sie alle Bytes untersuchen, um festzustellen, ob der letzte Codepunkt 0xxxxxxx oder (10xxxxxx, 0xxxxxxx) ist.

Es gibt tatsächlich effizientere Codierungsschemata, bei denen das Wechseln zum vorherigen Codepunkt in konstanter Zeit erfolgen kann und Zeiger auf die Mitte eines Codepunkts festgelegt werden können. Erlaube die folgenden Codes:

X where X < 128
YX where 128 ≤ Y < 236, X < 128
ZYY where 236 ≤ Z < 256, 0 ≤ Y < 236. 

Wenn eines der vorherigen drei Bytes ≥ 236 ist, ist dies der Beginn einer 3-Byte-Sequenz, da es in einer gültigen 3-Byte-Sequenz keine zwei solchen Bytes geben kann. Wenn andernfalls eines der vorherigen zwei Bytes ≥ 128 ist, ist dies der Beginn einer Zwei-Byte-Sequenz. Andernfalls ist das vorherige Byte ein einzelnes Byte <128.

Das Suchen nach einer Teilzeichenfolge wird etwas schwieriger. Möglicherweise möchten Sie null Bytes ausschließen, damit eine Zeichenfolge nur dann ein Nullbyte enthält, wenn sie einen Nullcodepunkt enthält.

gnasher729
quelle
Was nicht erwähnt wurde ... - nicht wirklich, da dies direkt aus der Beobachtung in der Antwort von @ratchet Freak folgt.
Piotr Dobrogost