Zum Inhalt springen

Warum sich Zeichenzahl und Bytezahl unterscheiden

Veröffentlicht: · Zuletzt aktualisiert:

Du fügst denselben Text in zwei Programme ein: Das eine zeigt 5 an, das andere 15. Keines von beiden irrt sich – sie zählen nur unterschiedliche Einheiten. Dieser Artikel stellt die vier Einheiten vor, in denen die Länge eines Textes gemessen wird, und zeigt, wie du herausfindest, welche Einheit hinter einer Zahl steckt.

Es gibt vier Zähleinheiten

  • Zeichen: das, was auf dem Bildschirm wie ein einzelnes Zeichen aussieht. Der Unicode-Standard nennt es Graphem-Cluster (grapheme cluster). Das ist die Einheit, die Menschen meinen, wenn sie „wie viele Zeichen“ fragen.
  • Codepoint: die Nummer, die Unicode jedem Zeichen zuweist. A ist U+0041, € ist U+20AC. Ein sichtbares Zeichen kann aus mehreren Codepoints bestehen.
  • UTF-16-Codeeinheit: die 16-Bit-Einheit, in der JavaScript und viele andere Umgebungen Zeichenketten intern ablegen. Die meisten Codepoints brauchen 1 Einheit, die ab U+10000 brauchen 2.
  • Byte: der Platz, den der Text tatsächlich belegt, wenn er in einer Datei gespeichert oder über ein Netzwerk gesendet wird. Er hängt von der Kodierung ab.
Zeichen Zeichen Codepoints UTF-16-Codeeinheiten UTF-8-Bytes
A 1 1 1 1
é 1 1 1 2
€ 1 1 1 3
😀 1 1 2 4

UTF-8-Bytes nach Art des Zeichens

UTF-8 verwendet 1–4 Bytes, je nachdem, in welchem Bereich der Codepoint liegt.

Codepoint-Bereich Bytes Was dazugehört Beispiele
U+0000–U+007F 1 Englische Buchstaben, Ziffern, einfache Zeichen (ASCII) A 7 !
U+0080–U+07FF 2 Lateinische Buchstaben mit Akzent, Griechisch, Kyrillisch und andere é ß α Ж
U+0800–U+FFFF 3 Hangul, chinesische Schriftzeichen, Kana, viele Symbole 한 字 あ €
U+10000–U+10FFFF 4 Die meisten Emojis, selten verwendete chinesische Schriftzeichen und andere 😀

Deshalb haben Texte mit gleich vielen Zeichen je nach Art der Zeichen unterschiedlich viele Bytes.

Zeichenkette Zeichen UTF-8-Bytes Rechnung
Hello 5 5 5 × 1
café 4 5 3 × 1 + 2
Größe 5 7 3 × 1 + 2 × 2
Привет 6 12 6 × 2
안녕하세요 5 15 5 × 3
こんにちは 5 15 5 × 3
Hi 😀 4 7 3 × 1 + 4

Warum ein Emoji, das wie ein Zeichen aussieht, viele Bytes belegt

Manche Emojis bestehen aus mehreren aneinandergefügten Codepoints, die als ein einziges Bild dargestellt werden.

Emoji Zeichen Codepoints UTF-16-Codeeinheiten UTF-8-Bytes Aufbau
👍🏽 1 2 4 8 Daumen U+1F44D + Hautton U+1F3FD
🇺🇳 1 2 4 8 Regionalindikatoren U+1F1FA + U+1F1F3
👨‍👩‍👧 1 5 8 18 3 Personen + die 2 ZWJ dazwischen

Das Familien-Emoji besteht aus den drei Emojis 👨 👩 👧, zwischen denen jeweils ein Verbinder ohne Breite (ZWJ, U+200D) steht. Ein Emoji hat 4 Bytes, ein ZWJ 3 Bytes – zusammen 3 × 4 + 2 × 3 = 18 Bytes. Ein Programm, das Zeichen zählt, zeigt 1 an, eines, das Codepoints zählt, 5, und eines, das UTF-16-Codeeinheiten zählt, 8.

Das betrifft nicht nur Emojis. é lässt sich als einzelner Codepoint schreiben (U+00E9, 2 Bytes) oder als e mit einem nachgestellten kombinierenden Akut (U+0301) – das sind 2 Codepoints (3 Bytes). Auf dem Bildschirm sieht beides gleich aus, die Bytezahl ist aber verschieden.

Warum dasselbe Zeichen je nach Kodierung unterschiedlich viele Bytes hat

Die Bytezahl legt nicht das Zeichen fest, sondern die Kodierung. Bevor sich UTF-8 durchsetzte, hatte jeder Sprachraum eigene Kodierungen, und diese brachten die Zeichen der eigenen Sprache in höchstens 2 Bytes unter.

Zeichen UTF-8 UTF-16 Ältere Kodierung
A 1 2 1
é 2 2 1 (windows-1252)
한 3 2 2 (EUC-KR)
あ 3 2 2 (Shift_JIS)
字 3 2 2 (EUC-KR, Shift_JIS)
😀 4 4 Nicht darstellbar

Darum liest man über dasselbe Zeichen sowohl „2 Bytes“ als auch „3 Bytes“. Die erste Angabe bezieht sich auf eine ältere Kodierung wie EUC-KR oder Shift_JIS, die zweite auf UTF-8. 한글 abc hat in UTF-8 10 Bytes und in EUC-KR 8 Bytes; Tokyo 東京 hat in UTF-8 12 Bytes und in Shift_JIS 10 Bytes. Heute neu entwickelte Systeme verwenden meist UTF-8, aber ältere Systeme und Formulare, die deren Regeln übernommen haben, zählen noch immer auf 2-Byte-Basis.

Was du in der Praxis zählen musst

Jede Stelle mit einem Limit zählt in ihrer eigenen Einheit. Bevor du einen Text auf eine Zahl kürzt, kläre, was gezählt wird.

  • Datenbankspalten: Bei manchen Produkten ist die Länge eine Zeichenzahl, bei anderen eine Bytezahl. Für varchar(n) in PostgreSQL steht in der Dokumentation, dass n eine Anzahl von Zeichen ist, nicht von Bytes. Sieh in der Dokumentation zu deinem Produkt und deinen Einstellungen nach.
  • Eingabefelder im Web: Das HTML-Attribut maxlength und length in JavaScript zählen UTF-16-Codeeinheiten. In ein Feld mit maxlength 10 passt 😀 nur 5-mal.
  • URLs und Base64: Beide wandeln UTF-8-Bytes um, die Länge des Ergebnisses richtet sich also nach der Bytezahl, nicht nach der Zeichenzahl. Das einzelne Zeichen € wird URL-kodiert zu %E2%82%AC (9 Zeichen) und in Base64 zu 4oKs (4 Zeichen).
  • Textnachrichten und Formulare mit Byte-Limit: Wie viel in eine Nachricht passt, hängt von der intern verwendeten Kodierung ab. Prüfe, ob die Einheit im Hinweistext Zeichen oder Bytes sind.

Ist die Einheit nicht angegeben, tippe einzelne Zeichen in ein Feld mit Zähler und beobachte, um wie viel die Zahl steigt.

Anstieg bei A / 字 / 😀 Gezählte Einheit
1 / 1 / 1 Zeichen oder Codepoints
1 / 1 / 2 UTF-16-Codeeinheiten
1 / 3 / 4 UTF-8-Bytes
1 / 2 / ein anderer Wert Älteres Verfahren auf 2-Byte-Basis

Zeichen und Codepoints unterscheidest du mit 👍🏽: Steigt die Zahl um 1, sind es Zeichen, steigt sie um 2, sind es Codepoints.

So zählst du selbst nach

Füge den Code unten in die Konsole der Entwicklertools deines Browsers ein, um alle vier Werte zu sehen. Ändere nur die Zeichenkette in der ersten Zeile.

const s = '👨‍👩‍👧';
[...new Intl.Segmenter().segment(s)].length; // 1: Zeichen
[...s].length;                               // 5: Codepoints
s.length;                                    // 8: UTF-16-Codeeinheiten
new TextEncoder().encode(s).length;          // 18: UTF-8-Bytes

Ohne Code geht es auch: Füge deinen Text in die verwandten Tools unten ein. Sie zeigen die Zeichenzahl, die UTF-8-Bytezahl und die Bytezahl auf 2-Byte-Basis nebeneinander an.

Quellen