HandyTools Hub

← Alle Anleitungen

Zeichenkodierung & Unicode erklärt: Von ASCII bis UTF-8, Mojibake und darüber hinaus

2026-08-06

Sie haben es hundertmal gesehen: Eine CSV, die in Excel als 涓枃 öffnet, eine Webseite, die é zeigt, wo é stehen sollte, Logs voller Fragezeichen, wo einst chinesischer Text lebte. Jedes dieser Rätsel hat dieselbe Grundursache — Zeichenkodierung. Es ist Wissen, das jeder Programmierer täglich nutzt, aber wenige je systematisch gelernt haben. Diese Anleitung geht den ganzen Bogen von ASCII zu Unicode, UTF-8, UTF-16, BOM und Normalisierung, damit Sie beim nächsten Textbruch genau wissen, wo Sie nachsehen.

ASCII und die Codepage-Ära

ASCII, in den 1960er Jahren definiert, kodiert 128 Zeichen in 7 Bits: englische Buchstaben, Ziffern, Satzzeichen und Steuerzeichen. A ist 0x41, a ist 0x61, Leerzeichen ist 0x20. Für eine englischsprachige Welt war das reichlich.

Die Welt spricht jedoch nicht nur Englisch. Ein Byte hat 8 Bits, es bleiben 128 freie Plätze über ASCII, und jede Region füllte sie mit eigenen Zeichen: Westeuropa nutzte Latin-1 (ISO-8859-1) für é und ü, Russland nutzte KOI8-R für Kyrillisch, und das chinesische Festland nutzte GB2312 und dessen Obermenge GBK, die zwei Bytes pro Zeichen braucht, weil ein Byte keine Tausende Hanzi halten kann. Diese Schemata hießen zusammen Codepages und hatten einen fatalen Fehler: Das Byte 0xE4 bedeutet ä in Latin-1, ist aber die Hälfte eines Hanzi in GBK. Bytes tragen keine Kennzeichnung, welche Kodierung sie erzeugt hat — in dem Moment, in dem der Decoder die falsche Codepage errät, erhalten Sie Müll. Unicode existiert, um dieses Chaos zu beenden.

Unicode: Eine Nummer pro Zeichen

Unicodes Idee ist wunderschön einfach: Vergessen Sie Bytes für einen Moment und geben Sie jedem Zeichen in jeder Schrift eine einzige eindeutige Nummer, Codepunkt genannt, geschrieben als U+ gefolgt von Hex. A ist U+0041, das Hanzi 中 ist U+4E2D, und 😀 ist U+1F600. Der Coderaum reicht von U+0000 bis U+10FFFF und deckt nahezu jede genutzte Schrift, jedes Symbol und jedes Emoji ab.

Hier ist das Denkmodell, das alles Weitere klick machen lässt: Ein Codepunkt ist eine abstrakte Zahl; Bytes sind das Speicherformat; die Abbildung zwischen ihnen ist die Kodierung. Zu sagen, eine Datei sei „in Unicode“, ist eigentlich bedeutungslos — Dateien enthalten immer Bytes, und was diese Bytes bestimmt, ist eine Kodierungsform wie UTF-8 oder UTF-16. Müssen Sie den Codepunkt eines Zeichens nachschlagen, zeigt der Unicode-Zeichenfinder seine Nummer, seinen offiziellen Namen und seine UTF-8-Bytefolge an einem Ort.

UTF-8: Variabel längenrichtig gemacht

UTF-8 ist die dominierende Kodierung im modernen Web, und sein Design ist elegant: Jeder Codepunkt braucht 1 bis 4 Bytes, wobei kleinere Codepunkte weniger Bytes nutzen.

U+0000 – U+007F:    0xxxxxxx                              (1 Byte)
U+0080 – U+07FF:    110xxxxx 10xxxxxx                     (2 Bytes)
U+0800 – U+FFFF:    1110xxxx 10xxxxxx 10xxxxxx            (3 Bytes)
U+10000 – U+10FFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx   (4 Bytes)

Nehmen Sie 中 (U+4E2D), das in den Drei-Byte-Bereich fällt. 4E2D binär expandiert ergibt 0100 111000 101101; setzt man diese Bits in das Template, ergibt das 11100100 10111000 10101101 — die Bytefolge E4 B8 AD. Jeder Codepunkt von U+0800 bis U+FFFF (einschließlich aller gängigen Hanzi) kostet 3 Bytes, und Emoji, die über U+FFFF liegen, kosten immer 4.

UTF-8s Killer-Feature ist die vollständige ASCII-Kompatibilität: Jedes ASCII-Zeichen kodiert genau zu seinem ursprünglichen Byte. Eine rein englische UTF-8-Datei ist Byte für Byte identisch mit einer ASCII-Datei, Alt-Werkzeuge lesen die englischen Teile also problemlos. Kombiniert mit Selbstsynchronisation (Sie können von jedem Byte aus die nächste Zeichengrenze finden) überrascht es nicht, dass UTF-8 gewann. Um die Zeichen-zu-Byte-Beziehung hands-on zu sehen, schicken Sie etwas Text in den Text-zu-Binär-Konverter und prüfen Sie dessen Darstellung in Hex, Dezimal oder Binär.

UTF-16, UCS-2 und Surrogate-Paare

UTF-16 geht einen anderen Kompromiss ein: Zeichen in der Basic Multilingual Plane (BMP, U+0000–U+FFFF) nutzen 2 Bytes, und alles über der BMP nutzt 4. Java- und JavaScript-Strings und Windows intern sind alle UTF-16.

Zeichen jenseits der BMP werden als Surrogate-Paare kodiert. Unicode reserviert U+D800–U+DFFF als Surrogat-Codepunkte, die nie echte Zeichen repräsentieren. Ein Astral-Codepunkt teilt sich in ein hohes Surrogat (U+D800–U+DBFF) gefolgt von einem niedrigen (U+DC00–U+DFFF). Zum Beispiel wird 😀 (U+1F600) zum Paar D83D DE00 — zwei 16-Bit-Code-Einheiten.

Das verursacht einen berühmten Stolperstein: JavaScripts "😀".length gibt 2 zurück, weil length UTF-16-Code-Einheiten zählt, nicht Zeichen. Die ältere UCS-2-Kodierung unterstützte Zeichen außerhalb der BMP überhaupt nicht, weshalb manche Altsysteme bestimmte Emoji einfach nicht anzeigen können. Beim Iterieren von Strings in modernem JS laufen for...of und der Spread-Operator über Codepunkte statt Code-Einheiten — der korrekte Standard.

Woher Mojibake kommt

Mojibake — der Fachbegriff für verstümmelten Text — hat im Wesentlichen zwei Ursachen.

Ursache eins: Dekodieren mit dem falschen Charset. Die Bytes E4 B8 AD dekodieren als UTF-8 zu 中, aber interpretiert ein Leser sie als GBK, wird E4 B8 zu einem Hanzi und AD klebt ans nächste Byte — genau so mutiert 中文 zum berüchtigten 涓枃. In die andere Richtung treffen GBK-Bytes einen UTF-8-Decoder auf ungültige Sequenzen und werden durch � (U+FFFD, das Ersatzzeichen) ersetzt. Das klassische é auf Webseiten ist derselbe Unfall: é in UTF-8 ist C3 A9, und diese zwei Bytes als Latin-1 gelesen erzeugen zwei getrennte Zeichen.

Ursache zwei: Doppelkodierung. Text wird korrekt als UTF-8 kodiert, dann werden diese Bytes für einen Latin-1-String gehalten und erneut als UTF-8 kodiert. é wird zuerst é, dann vier Bytes, und die Verstümmelung verstärkt sich mit jedem Roundtrip. Die Lösung ist, den Prozess umzukehren: als Latin-1 zurück zu Bytes dekodieren, dann als UTF-8 dekodieren — manchmal zweimal.

Ein praktischer Trick beim Diagnostizieren von Mojibake: Fügen Sie die verstümmelten Zeichen in den Unicode-Zeichenfinder ein und schauen Sie auf ihre Codepunkte — das Muster verrät meist die ursprüngliche Kodierungskette. Und ist der Text mit HTML-Entities wie é oder 中 übersät, ist das eine separate Escaping-Schicht; dekodieren Sie sie zuerst mit dem HTML-Encoder/Decoder, bevor Sie den zugrunde liegenden Text beurteilen.

Die BOM und warum UTF-8-BOM beißt

Eine UTF-16-Datei, die mit den Bytes 4E 2D beginnt, ist mehrdeutig: Ist das 中 in Big-Endian-Reihenfolge oder U+2D4E in Little-Endian? Die BOM (Byte Order Mark) löst das: Kodieren Sie den Codepunkt U+FEFF am Dateianfang. UTF-16 Big-Endian schreibt FE FF, Little-Endian FF FE, und die ersten zwei Bytes sagen Ihnen die Reihenfolge.

UTF-8 hat kein Byte-Reihenfolge-Problem, doch eine UTF-8-BOM — die drei Bytes EF BB BF — wurde trotzdem erfunden als „das ist UTF-8“-Signatur, weitgehend popularisiert durch Windows Notepad. Der Ärger ist, dass viele Werkzeuge und Protokolle drei Extra-Bytes am Anfang nicht erwarten:

  • Eine PHP-Datei mit BOM erzeugt Ausgabe, bevor header() läuft, und löst „headers already sent“ aus;
  • Eine BOM vor dem #!-Shebang eines Shell-Skripts bedeutet, dass der Kernel den Interpreter nicht findet;
  • CSV-Parser behandeln die BOM womöglich als Teil des ersten Feldinhalts;
  • Das Aneinanderhängen von Dateien schiebt BOMs in die Textmitte, wo sie als unsichtbare Zero-Width-Zeichen lauern.

Faustregel: Schreiben Sie UTF-8 standardmäßig ohne BOM, außer Sie wissen, dass der Konsument — bestimmte Excel-Versionen beim Öffnen von UTF-8-CSVs sind die übliche Entschuldigung — sie wirklich braucht.

Normalisierung: Ist é ein Zeichen oder zwei?

Unicode erlaubt viele akzentuierte Zeichen auf zwei äquivalente Weisen. é kann der einzelne Codepunkt U+00E9 (vorkomponiert) sein, oder der Buchstabe e (U+0065) gefolgt von einem kombinierenden Akut U+0301. Beide rendern identisch, vergleichen sich aber ungleich Byte für Byte und Codepunkt für Codepunkt. Dateisysteme, eindeutige Datenbankindizes und Passwortvergleiche wurden alle gebissen: Zwei Strings, die für einen Menschen identisch aussehen, sind für das Programm verschieden.

Unicode definiert Normalisierungsformen, um das zu beheben:

  • NFC (Canonical Composition): alles Zusammenführbare zusammenführen — e + U+0301 wird U+00E9;
  • NFD (Canonical Decomposition): alles Trennung fähige trennen — U+00E9 wird e + U+0301;
  • NFKC/NFKD wenden zusätzlich Kompatibilitätsabbildungen an und machen die fi-Ligatur zu fi und Vollbreitenzeichen zu Halbbreitenzeichen. Diese verlieren Information, nutzen Sie sie also zum Matchen, nicht zum Speichern.

Praktischer Rat: Normalisieren Sie Nutzereingaben zu NFC, bevor Sie sie speichern, und stellen Sie sicher, dass beide Seiten jedes Vergleichs, Dedups oder Hashes dieselbe Form nutzen. Eine reale Falle: macOS’ Dateisystem neigt zu NFD, während Windows und der Großteil der Welt NFC nutzen, Dateinamen können sich also beim Plattformwechsel subtil ändern.

Eine praktische Debugging-Checkliste

Wenn Kodierungsärger zuschlägt, versagt diese Reihenfolge fast nie:

  1. Prüfen Sie zuerst die tatsächliche Kodierung der Datei. Unter Linux/macOS gibt file input.txt einen schnellen Hinweis; zur Gewissheit zeigt hexdump -C file | head die Anfangsbytes — EF BB BF heißt UTF-8-BOM, FF FE heißt UTF-16 Little-Endian.
  2. Prüfen Sie bei verstümmelten Webseiten die Deklarationen. Der HTTP-Antwort-Header Content-Type: text/html; charset=utf-8 rangiert über dem <meta charset="utf-8">-Tag im HTML. Eine Abweichung zwischen beiden ist der klassische Grund, warum eine Seite lokal gut aussieht, aber in der Produktion bricht.
  3. Zählen Sie Bytes, um die Kodierung zu erraten. In UTF-8 nehmen Hanzi 3 Bytes, ASCII 1, Emoji 4; in GBK nehmen Hanzi 2. Lange Läufe von Drei-Byte-Sequenzen, die mit E4–E9 beginnen, sind ein starker Hinweis auf chinesisches UTF-8.
  4. Konvertieren Sie ausdrücklich, statt zu raten. Ist die Quellkodierung bekannt, führen Sie iconv -f GBK -t UTF-8 input.txt aus, statt der Auto-Erkennung eines Editors zu vertrauen.
  5. Dekodieren Sie Schicht für Schicht. Wickeln Sie HTML-Entities mit dem HTML-Encoder/Decoder ab, bestätigen Sie den wahren Codepunkt jedes Zeichens mit dem Unicode-Zeichenfinder und prüfen Sie die Rohbytes mit dem Text-zu-Binär-Konverter — inspizieren Sie alle drei Schichten, und kein Kodierungs-Bug kann sich verstecken.

Kodierungs-Bugs sind heimtückisch, gerade weil nie ein Fehler geworfen wird — Sie bekommen einfach still falschen Text. Aber haben Sie einmal verinnerlicht, dass Zeichen Codepunkte sind, Dateien Bytes und Kodierungen die Abbildungen dazwischen — plus die zwei Fußnoten BOM und Normalisierung —, wird fast jedes Mojibake, dem Sie je begegnen, eine Zehn-Minuten-Diagnose statt eines Nachmittags Verzweiflung.