Ratgeber · 7 Min Lesezeit

Von ASCII zu Unicode: Zeichenkodierung im Wandel

Wie Buchstaben zu Bytes werden, warum Umlaute manchmal als komische Symbole erscheinen und was UTF-8 mit Variable-Length-Codes zu tun hat.

Wenn Sie das hier lesen, schaut Ihr Computer in Wirklichkeit auf eine Folge von Zahlen. Ein „A” ist intern keine Form, sondern die Zahl 65. Welche Zahl welchem Zeichen entspricht, regeln Zeichenkodierungen. Die Geschichte dieser Kodierungen ist die Geschichte einer langsamen Annäherung an die globale Sprachvielfalt, und sie ist überraschend voller Stolperfallen.

ASCII: 128 Zeichen für die englische Welt

1963 standardisierte das American Standards Institute den ASCII-Code, kurz für American Standard Code for Information Interchange. Er belegt 7 Bit pro Zeichen und kennt damit 128 Codes:

  • 0 bis 31: Steuerzeichen (Tabulator, Zeilenumbruch, „Klingelzeichen” 7)
  • 32: Leerzeichen
  • 33 bis 47: Satzzeichen und Symbole
  • 48 bis 57: Ziffern 0 bis 9
  • 65 bis 90: Großbuchstaben A bis Z
  • 97 bis 122: Kleinbuchstaben a bis z
  • 127: DEL (Steuerzeichen, historisch zum Überschreiben gedacht)

Die Aufteilung wirkt willkürlich, hat aber System. Großbuchstaben und Kleinbuchstaben unterscheiden sich genau im sechsten Bit: A ist 01000001, a ist 01100001. Wer einen Buchstaben in Kleinschreibung wandeln will, muss nur dieses eine Bit setzen, mit OR.

Probleme tauchten schnell auf: Was ist mit ä, ö, é, ß, Á, ñ? ASCII konnte das nicht. Außerdem hatten Schreibmaschinen schon längst 8-Bit-Zeichen, das achte Bit war bei ASCII einfach ungenutzt.

Die ISO-8859-Familie: regionale Erweiterungen

Mit dem achten Bit ließen sich 128 weitere Codes vergeben. Daraus entstanden die ISO-8859-Kodierungen: ISO-8859-1 (Westeuropa, kennt ü, ä, ö, ß, é, ñ), ISO-8859-2 (Mitteleuropa, mit polnischen und tschechischen Diakritika), ISO-8859-5 (Kyrillisch), ISO-8859-7 (Griechisch) und so weiter. Jede Region bekam ihre Variante.

Das Problem: dieselbe Bytefolge bedeutete in unterschiedlichen Kodierungen unterschiedliches. Eine deutsche Datei mit „Mädchen” in ISO-8859-1 wurde in ISO-8859-2 zu „Mądchen”, weil das Byte für ä in einer Kodierung dem für ą in der anderen entspricht. Ohne explizite Angabe der Kodierung mussten Programme raten, oft falsch.

Asiatische Sprachen mit Tausenden von Zeichen passten ohnehin nicht in 256 Codes. Sie nutzten Multi-Byte-Kodierungen wie Shift-JIS (Japanisch) oder GB 2312 (Chinesisch), die zwischen Ein- und Zwei-Byte-Sequenzen wechselten. Ein Text-Editor, der nicht wusste, in welcher Kodierung er lesen sollte, zeigte Müll.

Unicode: ein Zeichensatz für alle

In den späten 1980ern begann das Unicode-Konsortium, eine universelle Zeichentabelle zu definieren. Jedes Zeichen jeder Sprache bekam einen eigenen Code, einen sogenannten Codepoint. Heute (Unicode 15) sind über 149.000 Zeichen vergeben, von lateinischen Buchstaben über chinesische Han-Zeichen bis zu Emoji.

Codepoints schreibt man als U+XXXX mit hexadezimaler Nummer. Beispiele:

  • A: U+0041
  • ä: U+00E4
  • π: U+03C0
  • 中: U+4E2D
  • 😀: U+1F600

Bemerkenswert: die ersten 128 Codepoints sind identisch mit ASCII. Wer ASCII liest, kann automatisch auch reinen englischen Unicode-Text lesen.

Unicode definiert nur die Zuordnung Zeichen → Codepoint, nicht, wie Codepoints in Bytes gespeichert werden. Dafür gibt es mehrere Kodierungsformen.

UTF-8: die durchgesetzte Form

UTF-8 wurde 1992 von Ken Thompson und Rob Pike (den Erfindern von Unix und Plan 9) entworfen und ist heute die dominante Webkodierung — über 98 Prozent aller Webseiten nutzen sie.

UTF-8 ist eine Variable-Length-Kodierung. Ein Zeichen kann 1 bis 4 Bytes belegen:

BytesBereichBitmuster
1U+0000 bis U+007F0xxxxxxx
2U+0080 bis U+07FF110xxxxx 10xxxxxx
3U+0800 bis U+FFFF1110xxxx 10xxxxxx 10xxxxxx
4U+10000 bis U+10FFFF11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

Zwei Eigenschaften machen UTF-8 elegant:

  • ASCII-kompatibel. Reine ASCII-Texte sind automatisch gültige UTF-8.
  • Selbst-synchronisierend. Jedes Byte verrät sofort, ob es ein Start-Byte oder ein Folge-Byte ist. Ein Parser kann an jeder Stelle wieder einsteigen.

Ein „ä” (U+00E4) wird in UTF-8 zu C3 A4, also zwei Bytes. Das ist der Grund, warum eine deutsche Datei in UTF-8 etwas größer ist als in ISO-8859-1.

UTF-16 und UTF-32

UTF-16 nutzt 2 oder 4 Bytes pro Zeichen. Windows-APIs und Java-Strings sind intern UTF-16. Es spart Platz bei asiatischen Texten, aber für lateinischen Text ist es doppelt so groß wie UTF-8.

UTF-32 belegt immer 4 Bytes pro Codepoint. Speicher-ineffizient, aber Index-Operationen sind trivial (Zeichen n liegt einfach an Position 4n). Wird selten als Speicherformat, manchmal aber intern für Verarbeitung verwendet.

Die Mojibake-Klassiker

Wer schon einmal „ä” statt „ä” gesehen hat, kennt das Phänomen: UTF-8-Bytes wurden als ISO-8859-1 interpretiert. Das „C3 A4” von „ä” wird in ISO-8859-1 zu zwei Zeichen, „Ô und „¤”. Solche Fehlinterpretationen heißen im Japanischen Mojibake, „verstümmelte Zeichen”.

Vermeidung: immer die Kodierung explizit angeben. HTML-Seiten mit <meta charset="utf-8">, HTTP-Antworten mit Content-Type: text/html; charset=utf-8, Datenbanken mit korrekter Collation, Quellcode-Dateien mit BOM oder Editor-Einstellung.

Praktische Konsequenzen

  • Stringlänge ist nicht gleich Bytelänge. Wer in C strlen() auf UTF-8 anwendet, bekommt die Anzahl der Bytes, nicht der Zeichen.
  • Substring nach Bytes kann Zeichen zerteilen. Ein UTF-8-Substring mit substr(0, 5) kann mitten in einer Multi-Byte-Sequenz enden.
  • Sortierung ist kulturabhängig. „ö” sortiert sich in Deutsch nach o, in Schweden nach z. Reine Codepoint-Sortierung ignoriert das.
  • Datenbanken brauchen die richtige Collation. utf8mb4_unicode_ci in MySQL etwa unterstützt sogar 4-Byte-Zeichen inklusive Emoji.

ASCII-Codes nachschlagen

In unserer Zahlenübersicht findet sich für jede Zahl von 32 bis 126 die ASCII-Bedeutung. Zahl 65 ist „A”, Zahl 97 ist „a”, und Zahl 42 ist das berühmte Sternchen. Wer wissen will, welches Bitmuster hinter einem Zeichen steckt, kann den Codepoint einfach in unserem Binär-Umrechner eingeben.

Verwandte Themen

Vertiefend zur Binärdarstellung: Binär zu Dezimal und Hexadezimal, zur Speicheranordnung von Multi-Byte-Werten: Endianness.

Anzeige
Anzeige
Anzeige
Anzeige
Anzeige