Ist UTF-16 eine feste Breite oder eine variable Breite? Warum hat UTF-8 kein Problem mit der Bytereihenfolge?

16
  1. Ist UTF-16 eine feste Breite oder eine variable Breite? Ich habe unterschiedliche Ergebnisse aus verschiedenen Quellen erhalten:

    Von http://www.tbray.org/ongoing/When/200x/2003/04/26/UTF :

    UTF-16 speichert Unicode-Zeichen in 16-Bit-Blöcken.

    Aus http://en.wikipedia.org/wiki/UTF-16/UCS-2 :

    UTF-16 (16-Bit-Unicode-Transformationsformat) ist eine Zeichencodierung für Unicode, mit der 1.112.064 [1] Zahlen (als Codepunkte bezeichnet) im Unicode-Codebereich von 0 bis 0x10FFFF codiert werden können. Es wird ein Ergebnis variabler Länge mit einer oder zwei 16-Bit-Codeeinheiten pro Codepunkt erzeugt.

  2. Aus der ersten Quelle

    UTF-8 hat auch den Vorteil, dass die Codierungseinheit das Byte ist, sodass es keine Probleme mit der Byte-Reihenfolge gibt.

    Warum hat UTF-8 kein Problem mit der Bytereihenfolge? Es ist variabel und ein Zeichen kann mehr als ein Byte enthalten. Ich denke also, dass die Reihenfolge der Bytes immer noch ein Problem sein kann.

Danke und Grüße!

Tim
quelle
Dieser großartige Artikel Das absolute Minimum, das jeder Softwareentwickler
unbedingt

Antworten:

13

(1) Was bedeutet Bytesequenz, ein arrary of char in C? Ist UTF-16 eine Bytesequenz oder was ist es dann? (2) Warum hat eine Bytefolge nichts mit variabler Länge zu tun?

Sie scheinen zu missverstehen, was Endian-Themen sind. Hier ist eine kurze Zusammenfassung.

Eine 32-Bit-Ganzzahl belegt 4 Bytes. Nun kennen wir die logische Reihenfolge dieser Bytes. Wenn Sie eine 32-Bit-Ganzzahl haben, können Sie das High-Byte mit dem folgenden Code abrufen:

uint32_t value = 0x8100FF32;
uint8_t highByte = (uint8_t)((value >> 24) & 0xFF); //Now contains 0x81

Das ist alles schön und gut. Wo das Problem beginnt, ist, wie verschiedene Hardware ganze Zahlen aus dem Speicher speichert und abruft.

In der Big-Endian-Reihenfolge wird ein 4-Byte-Speicher, den Sie als 32-Bit-Ganzzahl lesen, gelesen, wobei das erste Byte das High-Byte ist:

[0][1][2][3]

In Little Endian-Reihenfolge wird ein 4-Byte-Speicher, den Sie als 32-Bit-Ganzzahl lesen, gelesen, wobei das erste Byte das untere Byte ist:

[3][2][1][0]

Wenn Sie einen Zeiger auf einen Zeiger auf einen 32-Bit-Wert haben, können Sie dies tun:

uint32_t value = 0x8100FF32;
uint32_t *pValue = &value;
uint8_t *pHighByte = (uint8_t*)pValue;
uint8_t highByte = pHighByte[0]; //Now contains... ?

Das Ergebnis ist laut C / C ++ undefiniert. Es könnte 0x81 sein. Oder es könnte 0x32 sein. Technisch könnte es alles zurückgeben, aber für echte Systeme wird es das eine oder andere zurückgeben.

Wenn Sie einen Zeiger auf eine Speicheradresse haben, können Sie diese Adresse als 32-Bit-Wert, 16-Bit-Wert oder 8-Bit-Wert lesen. Auf einer Big-Endian-Maschine zeigt der Zeiger auf das High-Byte. Auf einer kleinen Endian-Maschine zeigt der Zeiger auf das niedrige Byte.

Beachten Sie, dass es hier nur um das Lesen und Schreiben in den / aus dem Speicher geht. Es hat nichts mit dem internen C / C ++ - Code zu tun. Die erste Version des Codes, die C / C ++ nicht als undefiniert deklariert, funktioniert immer , um das High-Byte zu erhalten.

Das Problem ist, wenn Sie mit dem Lesen von Byte-Streams beginnen. Wie aus einer Datei.

16-Bit-Werte haben dieselben Probleme wie 32-Bit-Werte. Sie haben nur 2 Bytes anstelle von 4. Daher kann eine Datei 16-Bit-Werte enthalten, die in Big-Endian- oder Little-Endian-Reihenfolge gespeichert sind.

UTF-16 ist als Folge von 16-Bit-Werten definiert . Tatsächlich ist es ein uint16_t[]. Jede einzelne Codeeinheit ist ein 16-Bit-Wert. Um UTF-16 korrekt laden zu können, müssen Sie daher die Endianität der Daten kennen.

UTF-8 ist als Folge von 8-Bit-Werten definiert . Es ist ein uint8_t[]. Jede einzelne Codeeinheit hat eine Größe von 8 Bit: ein einzelnes Byte.

Sowohl in UTF-16 als auch in UTF-8 können mehrere Codeeinheiten (16-Bit- oder 8-Bit-Werte) zu einem Unicode-Codepunkt (einem "Zeichen") kombiniert werden, dies ist jedoch nicht der richtige Begriff, sondern eine Vereinfachung ). Die Reihenfolge dieser Codeeinheiten, die einen Codepunkt bilden, wird durch die UTF-16- und UTF-8-Codierungen bestimmt.

Bei der Verarbeitung von UTF-16 lesen Sie einen 16-Bit-Wert und führen die erforderliche Endian-Konvertierung durch. Dann stellen Sie fest, ob es sich um ein Ersatzpaar handelt. Wenn dies der Fall ist, lesen Sie einen weiteren 16-Bit-Wert, kombinieren die beiden und erhalten daraus den Unicode-Codepunktwert.

Bei der Verarbeitung von UTF-8 lesen Sie einen 8-Bit-Wert. Eine Endian-Konvertierung ist nicht möglich, da nur ein Byte vorhanden ist. Wenn das erste Byte eine Multibyte-Sequenz angibt, lesen Sie eine bestimmte Anzahl von Bytes, die von der Multibyte-Sequenz vorgegeben wird. Jedes einzelne Byte ist ein Byte und hat daher keine Endian-Konvertierung. Die Reihenfolge dieser Bytes in der Sequenz wird ebenso wie die Reihenfolge der Ersatzpaare in UTF-16 durch UTF-8 definiert.

Es kann also keine Endian-Probleme mit UTF-8 geben.

Nicol Bolas
quelle
10

Die Antwort von Jeremy Banks ist zwar richtig, hat aber die Bytereihenfolge nicht berücksichtigt.

Wenn Sie UTF-16 verwenden, werden die meisten Glyphen mit einem Zwei-Byte-Wort gespeichert. Wenn das Wort jedoch in einer Festplattendatei gespeichert wird, in welcher Reihenfolge speichern Sie die einzelnen Bytes?

Das CJK-Zeichen (Chinesisch) für das Wort "Wasser" hat beispielsweise eine UTF-16-Codierung in hexadezimaler Schreibweise von 6C34. Wenn Sie das als zwei Bytes auf die Festplatte schreiben, schreiben Sie es als "Big-Endian" (die zwei Bytes sind 6C 34)? Oder schreiben Sie es als "Little-Endian" (die beiden Bytes sind 34 6C)?

Bei UTF-16 sind beide Ordnungen legitim, und Sie geben normalerweise an, welche die Datei hat, indem Sie das erste Wort in der Datei zu einer Byte Order Mark (BOM) machen, die für die Big-Endian-Codierung FE FF und für Little-Endian ist Kodierung ist FF FE.

UTF-32 hat das gleiche Problem und die gleiche Lösung.

UTF-8 hat dieses Problem nicht, weil es eine variable Länge hat und Sie effektiv die Byte-Sequenz eines Glyphen schreiben, als ob es ein Little-Endian wäre. Beispielsweise wird der Buchstabe "P" immer mit einem Byte codiert - 80 - und das Ersatzzeichen wird immer mit den zwei Bytes FF FD in dieser Reihenfolge codiert.

Einige Programme setzen einen Drei-Byte-Indikator (EF BB BF) am Anfang einer UTF-8-Datei. Dies hilft dabei, UTF-8 von ähnlichen Kodierungen wie ASCII zu unterscheiden. Dies kommt jedoch nur unter MS Windows häufig vor.

Bob Murphy
quelle
Vielen Dank! (1) Der Buchstabe "P" ist nur ein Byte in UTF-8. Warum wird das Ersatzzeichen zu seinem Code hinzugefügt? (2) In UTF-8 gibt es andere Zeichen, die in UTF-8 mehr als ein Byte enthalten. Warum ist die Bytereihenfolge zwischen den Bytes für jedes dieser Zeichen kein Problem?
Tim
@Tim: (1) Sie fügen dem Code für P kein Ersatzzeichen hinzu. Wenn 80 FF FD angezeigt wird, sind dies zwei Zeichen - ein P-Zeichen und ein Ersatzzeichen.
Bob Murphy
(2) Sie schreiben und lesen immer die zwei Bytes für das "Ersatzzeichen" als FF FD in dieser Reihenfolge. Es gäbe nur ein Problem mit der Bytereihenfolge, wenn Sie das "Ersatzzeichen" auch als FD FF schreiben könnten - aber Sie können nicht; Diese Folge von zwei Bytes wäre etwas anderes als ein "Ersatzzeichen".
Bob Murphy
1
@Tim: Möglicherweise möchten Sie die Datei en.wikipedia.org/wiki/UTF-8 durcharbeiten . Es ist wirklich ziemlich gut und wenn Sie alles und die anderen Unicode-bezogenen Wikipedia-Seiten verstehen, werden Sie wahrscheinlich keine weiteren Fragen dazu haben.
Bob Murphy
4
Der Grund, warum UTF-8 kein Problem mit der Bytereihenfolge hat, ist, dass die Codierung als Bytesequenz definiert ist und dass es keine Variationen mit unterschiedlicher Endianzahl gibt. Es hat nichts mit variabler Länge zu tun.
Starblue