Was man über Unicode wissen sollte

the next big thing Golo Roden  –  304 Kommentare

Nahezu jede Entwicklerin und jeder Entwickler kennt Unicode, zumindest vom Hörensagen. Doch vielen ist nicht klar, was genau Unicode eigentlich ist, was Encodings sind, und wie das alles im Detail funktioniert. Was sollte man über Unicode wissen?

Computer arbeiten, wie allgemein bekannt, lediglich mit zwei Zuständen, die in der Regel als 0 und 1 bezeichnet werden. Eine solche Informationseinheit wird als "Bit" bezeichnet, was sich aus den beiden Wörtern "binary" und "digit" zusammensetzt. Acht Bit wiederum werden zu einem Byte zusammengefasst, das auf dem Weg 256 verschiedene Werte repräsentieren kann.

Damit eignet sich ein einzelnes Byte hervorragend, um Zeichen zu speichern, seien es Buchstaben, Ziffern, Sonder- oder Satzzeichen. Damit Computer untereinander Daten austauschen können, benötigt man allerdings einen Standard, der jedem Zeichen einen Zahlencode zuordnet. Ein solches System wurde 1963 unter dem Namen "ASCII" vorgestellt, was für "American Standard Code for Information Interchange" steht.

Der ASCII-Code verwendet allerdings nur sieben Bit, was 128 mögliche Zeichen ergibt. Die ersten 32 Zeichen sind dabei für Steuerzeichen wie Tabulator, Linefeed oder Carriage-Return reserviert, weshalb 96 Zeichen für Buchstaben, Zahlen und Satzzeichen bleiben. Ein großes "A" wird in ASCII beispielsweise als 65 codiert, ein kleines "A" hingegen als 97. Die Leerstelle beispielsweise entspricht dem ASCII-Code 32.

Solange man sich nur im englischen Sprachraum bewegt, sind damit alle gängigen Texte abbildbar, doch was ist mit anderen Sprachen? Was ist mit sprachspezifischen Sonderzeichen wie den Umlauten im Deutschen? Man begann, dafür das achte Bit zu nutzen: Auf dem Weg wurde der Zeichenvorrat von 128 auf 256 Zeichen erweitert und es konnten sprachspezifische Zeichen untergebracht werden.

Allerdings wurde für jede Sprache eine eigene Abbildung entwickelt, so entspricht beispielsweise der Code 129 im Deutschen einem kleinen "Ü", im Griechischen hingegen dem großen "Beta". Für Russisch und verschiedene asiatische Sprachen gab es sogar mehrere Abbildungen. Diese Anstrengungen wurden als Codepages unter dem ANSI-Standard genormt. Für Deutsch gilt beispielsweise die Codepage 437, für Griechisch die Codepage 737.

Allen Codepages ist gemein, dass die ersten 128 Zeichen identisch sind, da sie dem ASCII-System entsprechen, die zweiten 128 Zeichen hingegen sind individuell belegt. Außerdem wurden einige wenige spezielle Codepages entwickelt, mit denen sich mehrere Sprachen gleichzeitig abbilden ließen. Da für viele asiatische Sprachen aber auch 128 sprachspezifische Zeichen zu wenig waren, wurden hier die sogenannten "Double Byte Character Sets" (DBCS) eingeführt, die ein Zeichen mit jeweils einem oder zwei Byte abbilden.

Was ist Unicode?

Codepoints in Unicode

Um diesem Wirrwarr aus Codepages und sprach- beziehungsweise landesspezifischen Vorgehensweisen ein Ende zu machen, wurde schließlich Unicode entwickelt. Die Kernidee dabei ist, ein Zeichen von seiner Repräsentation zu trennen: Ein großes "A" ist ein großes "A", unabhängig von der verwendeten Schriftart oder -stärke. Es geht also sozusagen um die Idee hinter dem Zeichen, nicht um dessen visuelle Darstellung.

Außerdem wurde festgelegt, was überhaupt als Zeichen gilt. In vielen Sprachen lässt sich diese Frage nicht so eindeutig beantworten, wie man intuitiv meinen könnte. Ist beispielsweise das "Ä" im Deutschen ein eigener Buchstabe oder lediglich eine ausgefallene Art, "AE" zu schreiben? Was ist mit dem "ß", das historisch gesehen für "sz" steht? Was ist mit Buchstaben, die am Wortende anders geschrieben werden als in der Wortmitte, wie das beispielsweise im Hebräischen und Arabischen der Fall ist?

Das alles wurde vom Unicode-Konsortium definiert, und in Folge jedem Zeichen ein sogenannter Codepoint zugeordnet. Dem großen "A" entspricht beispielsweise der Codepoint U+0041. Ein Codepoint ist dabei nichts anderes als ein numerischer Identifikator für ein Zeichen. Unicode unterstützt dabei über eine Million Zeichen, also weit mehr als die mit zwei Bytes darstellbaren 65.536 Möglichkeiten. Eine Zeichenkette lässt sich auf dem Weg als Folge von Codepoints abbilden. Das Wort "Hallo" entspricht beispielsweise den Codepoints:

U+0048 U+0061 U+006C U+006C U+006F

Will man eine solche Abfolge von Codepoints speichern, gibt es verschiedene Möglichkeiten, wie man vorgehen kann. Hier kommen Encodings ins Spiel.

Codepoints in Unicode

Encodings in Unicode

Die einfachste Option besteht darin, jeweils zwei hexadezimale Ziffern als ein Byte aufzufassen, was die Bytefolge

00 48 00 61 00 6C 00 6C 00 6F

ergibt. Das entspricht der Art, wie High-Endian-Systeme tatsächlich vorgehen. Ein Low-Endian-System hingegen würde die Bytefolge wie folgt speichern:

48 00 61 00 6C 00 6C 00 6F 00

Eine Bytecodierung ist also schon nicht mehr eindeutig, wenn man nicht die Endianness des ursprünglich verwendeten Systems kennt. Um das unterscheiden zu können, wurde in Unicode die sogenannte Byte Order Mark (BOM) eingeführt, die entweder als "FE FF" oder als "FF FE" an den Anfang eines Dokuments geschrieben wird.

Die unterschiedliche Codierung ist jedoch nicht das einzige Problem bei dieser Vorgehensweise: Das System ist inkompatibel zu ASCII und verschwendet durch die vielen Null-Bytes unnötig viel Speicher. Deshalb wurde UTF-8 entwickelt, ein Standard, der Zeichen durch eine variable Anzahl von Bytes darstellt: Die ersten 128 Zeichen werden durch ein einzelnes Bytes repräsentiert, alle nachfolgenden durch zwei bis sechs Bytes. Für englische Texte ist es also möglich, die Null-Bytes wegzulassen, wodurch UTF-8 für englische Texte weitaus platzsparender und außerdem kompatibel zu ASCII ist.

Einerseits ist UTF-8 also enorm praktisch, andererseits gibt es bereits jetzt schon drei verschiedene Wege, Unicode darzustellen: UCS-2-HE, UCS-2-LE und UTF-8. Und es gibt noch mehr Encodings, beispielsweise UTF-7 und UCS-4.

Encodings in Unicode

Unicode in der Praxis

Was man sich so oder so bewusst machen muss, ist, dass es bei Dokumenten keinen Plain-Text gibt, sondern lediglich Raw-Bytes. Ohne die Kenntnis des passenden Encodings können Dokumente, die Sonderzeichen enthalten, nicht sinnvoll und verlässlich angezeigt oder gedruckt werden. Deshalb ist es wichtig, das Encoding irgendwie anzugeben.

Bei Webseiten erfolgt das beispielsweise durch den "content-type"-Header. Fehlt dieser, müssen Webbrowser auf Heuristiken zurückgreifen, was aus naheliegenden Gründen nicht immer funktioniert. Schlägt ein solcher Versuch fehl, werden Webseiten fehlerhaft angezeigt. Gleiches gilt natürlich auch für Textdokumente, für E-Mails und andere Dateien.

Auch bei der Entwicklung von Software ist es wichtig, Encodings anzugeben. Lädt man beispielsweise in Node.js eine Datei mit Hilfe der fs.readFile-Funktion, muss zwingend ein Encoding angegeben werden, um einen Text zu erhalten – fehlt das Encoding, erhält man lediglich einen Buffer, der ein Array von Bytes darstellt.

Allerdings gibt es durchaus Situationen, in denen ein reiner Buffer ausreicht. Das ist zum Beispiel dann der Fall, wenn nur Bytes verarbeitet werden sollen, wie bei der Verschlüsselung von Daten. Spätestens wenn Text aber zur Anzeige kommt, muss das Encoding bekannt sein.

Aufpassen muss man bei der Verarbeitung von Streams, da ein Zeichen potenziell durch mehrere Bytes dargestellt wird und nicht garantiert ist, dass ein Chunk stets vollständige Zeichen enthält. Auch das Iterieren durch eine Zeichenkette oder das Ermitteln der Länge eines Texts in Zeichen wird schwieriger, da die Anzahl der Zeichen nicht mehr zwingend der Anzahl der Bytes entspricht.

Unicode in der Praxis

Eigene Zeichen in Unicode

Obwohl Unicode zentral vom Unicode-Konsortium verwaltet wird, gibt es Bereiche für den privaten Gebrauch, was unter anderem von Betriebssystemen und verschiedenen Anwendungen genutzt wird. Die Grundlage dafür bildet die Einteilung von Unicode in insgesamt 17 Planes, die jeweils 65.536 Zeichen umfassen und ihrerseits nochmals in Blöcke unterteilt werden.

Plane 0 ist dabei die sogenannte Basic Multilingual Plane (BMP), Plane 1 enthält historische Zeichen und Sonderzeichen für Kartenspiele, Mah-Jongg und ähnliche Zwecke, Plane 2 schließlich enthält Zeichen für asiatische Sprachen. Die Planes 3 bis 14 werden derzeit nicht verwendet, Plane 15 und 16 schließlich sind für den privaten Gebrauch freigegeben. Man spricht hier von der sogenannten Private Use Area A und Private Use Area B.

Wichtig bei der Verwendung dieses PUAs ist aber natürlich, die Codierung mit allen Beteiligten abzusprechen, damit alle die gleiche Darstellung erhalten. Tatsächlich legen in diesem Bereich die verschiedenen Betriebssysteme ihre jeweiligen individuellen Sonderzeichen ab – machen kann das aber potenziell jeder.

Eigene Zeichen in Unicode

Fazit

Unicode bildet die Grundlage für den Datenaustausch in der modernen, globalen Welt. Ohne Unicode wäre es nicht möglich, Webseiten oder Dokumente problemlos anzuzeigen, zu drucken oder anderweitig zu verarbeiten, die in fremden Sprachen verfasst wurden. Insofern kommt Unicode eine große Rolle im Alltag nicht nur jeder Entwicklerin und jedes Entwicklers ein, sondern auch jeder Anwenderin und jedes Anwenders.

Deshalb ist es nur naheliegend, sich mit dem Thema näher zu befassen – andernfalls wird es spätestens im nächsten Projekt schwierig, das mit in verschiedenen Sprachen verfassten Dokumenten zurechtkommen muss.