Skip to content

Unicode, UTF-8 und ein bisschen Python

Vergleich Unicode und andere Zeichenkodierungen

Unicode ist ein internationaler Standard, der es ermöglichen soll, jeden Text elektronisch speichern zu können. Also beispielsweise auch russische, chinesische, indische und klingonische Texte. Daher ist Unicode eine Zusammenführung und Erweiterung verschiedenster Zeichensatzkodierungen. Unicode geht jedoch für die Abbildung Buchstabe -> Bitfolge einen anderen weg als die meisten anderen, älteren Kodierungen. Unicode führt das Konzept des Code Points ein. Jeder Buchstabe ist eineindeutig auf einen solchen Codepoint abgebildet. Ein Codepoint entspricht einer Zahl in hexadezimaler Schreibweise: Man schreibt U+00DF und meint damit ß. U+ gibt an, dass es sicht bei der nachfolgenden Zeichenkombination um einen Codepoint handelt. Ein solcher Codepoint ist dabei immer noch eine Abstraktion, eine Zahl, die für einen Buchstaben steht. Wie diese Zahl elektronisch repräsentiert wird, wird durch die Zeichensatzkodierung angegeben. Und das ist der Unterschied zu anderen Zeichensätzen. Das Bild rechts soll das ein wenig deutlicher machen.

Es gibt verschiedene Möglichkeiten Unicode-Codepoints zu kodieren. Diese Abbildungen von Unicode auf Bitfolgen werden UTF (Unicode Transformation Format) genannt. Die wichtigsten sind wohl UTF-16, UCS-2 und UTF-8. UTF-16 repräsentiert Codepoints unterhalb von dezimal 65536 (hex 0×10000) als 16-bit Folge. Für Codepoints >= 65536 werden 32 Bit benötigt. Um hier eine 16-bit Repräsentation von einer 32-bittigen unterscheiden zu können ist festgelegt, dass im Raum D800 bis DFFF keine 16-bittigen Darstellungen gespeichert werden. So wird der Codepoint U+10FFFD, die obere Grenze von Unicode, in UTF-16 als DBFF DFFD dargestellt. Eine Anleitung zur Umrechnung von Codepoint in UTF-16-Darstellung findet sich im Eintrag zu UTF-16 auf Wikipedia.

UTF-16 verwendet also (im Regelfall) 16 Bit zur Darstellung eines Codepoints. UCS-2 verwendet 2 Byte. Auf vielen Rechnerarchitekturen sind diese beiden Darstellung daher identisch.

UTF-8 ist wahrscheinlich die interessanteste, weil am weitesten verbreitete, Kodierung. UTF-8 ist allerdings auch aus technischer Sicht interessant: Es ist eine verlustlose Zeichenkodierung, die Bit-Darstellungen unterschiedlicher Länge, von einem bis vier Bytes verwendet. ASCII-Zeichen können auch hier in einem Byte kodiert werden, sie benötigen ja nur 7 Bit. Alle Codepoints darüber werden in mehreren Bytes kodiert, wobei auch hier eine feste Bitfolge verwendet wird, um die Darstellung von Codepoints zu markieren, die sich über mehrere Bytes erstrecken. Das Schema ist ziemlich einfach (Quelle: UTF-8):

Codepoint-Bereich Darstellung in UTF-8
000000 – 00007F 0xxxxxxx
000080 – 0007FF 110xxxxx 10xxxxxx
000800 – 00FFFF 1110xxxx 10xxxxxx 10xxxxxx
010000 – 10FFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

Die x stehen dabei für die binäre Darstellung eines Codepoints. Man sieht, dass die Anzahl der 1en zu Beginn eines Blocks die länge der Darstellung angibt. 11110 bedeutet also beispielsweise, dass die Darstellung alle von UTF-8 zur Verfügung gestellten 4 Byte benötigt. Die Bytes, die ebenfalls noch zur Darstellung gehören, beginnen mit 10.

Um jetzt einen Codepoint in seine UTF-8-Darstellung umzuwandeln geht man wie folgt vor:

  • Angenommen, der Codepoint ist U+00DF (das ß von oben). Binär wird 00DF dargestellt als 11011111
  • 00DF ist im Bereich 000080 – 0007FF, es werden also zwei Byte für die Darstellung benötigt.
  • Jetzt werden die x mit der binären Darstellung ersetzt und ggf. vorne mit 0en aufgefüllt: 11000011 10011111
  • Der Codepoint 0x00DF entspricht also in der UTF-8-Kodierung dem Wert 0xC39F.

Im DenkzeitWiki habe ich auf der Seite UniCode weiterführende Links mit detaillierteren Erklärungen zusammengetragen.

Unicode und Python

Python verträgt sich recht gut mit UniCode. Auf PythonXML habe ich einige Links zusammengetragen, die das Thema behandeln. Ich möchte hier eine kurze Zusammenfassung geben.

Mit u'irgendein text' wird in Python ein Unicode-kodierter String erzeugt. Intern speichert Python Unicode-Strings im UCS-2-Format, als 16-bit unsigned integers.

Ein normaler String 'ein anderer String' wird im Default-Encoding kodiert, was default-mässig ASCII ist.

Man kann Unicode- und normale Strings mischen: u'ein ' + 'String' ergibt u'ein String'. Koersion macht aus dem normalen String also Unicode.

Unicode-Strings bieten die Methode encode. Sie wandelt den Unicode-String in die Kodierung um, die der Methode als Parameter übergeben wurde. Also beispielsweise: u'ein unicode-String'.encode('utf-8'). Eine Eingabe wie u'ein unicode-String mit ä'.encode('ascii') liefert demzufolge eine Exception UnicodeEncodeError, da in ASCII Umlaute nicht kodiert werden können.

Normale Strings bieten die Methode decode. Durch deren Aufruf wird der normale String in einen Unicode-String umgewandelt. Das Encoding des Strings muss dabei angegeben werden:

>>> string = u'ein unicode-String mit ä'.encode('utf-8')
>>> string
'ein unicode-String mit xc3xa4'
>>> string.decode('utf-8')
u'ein unicode-String mit xe4'

An diesem Beispiel sieht man schön, wie UTF-8 funktioniert: Das ä hat den Codepoint U+00E4, binär: 11100100. U+00E4 liegt im Codepoint-Bereich, der mit zwei Byte kodiert werden muss, also dem Schema 110xxxxx 10xxxxxx folgt: 11000011 10100100. Und das entspricht der hexadezimalen Darstellung: 0xC3A4, wie in 'ein unicode-String mit xc3xa4'. Bei der Umwandlung in UCS-2 fallen die Bits zur Markierung von Mehr-Bytigen Zeichen weg, es bleibt 0xE4 (= ä). Schön, oder nicht?

Post a Comment

Your email is never published nor shared. Required fields are marked *