Unicode-HOWTO
*************

Dieses HOWTO behandelt die Unterstützung der Unicode-Spezifikation zur
Darstellung von Textdaten in Python und erklärt verschiedene Probleme,
auf die man bei der Arbeit mit Unicode häufig stößt.


Einführung in Unicode
=====================


Begriffsbestimmungen
--------------------

Heutige Programme müssen eine große Vielfalt von Zeichen verarbeiten
können. Anwendungen werden oft internationalisiert, um Meldungen und
Ausgaben in einer Reihe vom Benutzer wählbarer Sprachen anzuzeigen.
Dasselbe Programm muss eine Fehlermeldung möglicherweise auf Englisch,
Französisch, Japanisch, Hebräisch oder Russisch ausgeben. Webinhalte
können in jeder dieser Sprachen geschrieben sein und auch eine
Vielzahl von Emoji-Symbolen enthalten. Der Zeichenkettentyp von Python
verwendet zur Darstellung von Zeichen den Unicode-Standard, sodass
Python-Programme mit all diesen möglichen Zeichen arbeiten können.

Unicode (https://www.unicode.org/) ist eine Spezifikation, die jedes
von menschlichen Sprachen verwendete Zeichen auflisten und jedem
Zeichen einen eigenen eindeutigen Code geben will. Die Unicode-
Spezifikationen werden fortlaufend überarbeitet und aktualisiert, um
neue Sprachen und Symbole zu ergänzen.

Ein **Zeichen** ist der kleinstmögliche Bestandteil eines Textes. „A“,
„B“, „C“ usw. sind alle verschiedene Zeichen. Ebenso „È“ und „Í“. Was
als Zeichen gilt, hängt von der Sprache oder dem Zusammenhang ab, über
den man spricht. So gibt es zum Beispiel ein Zeichen für „Roman
Numeral One“, „Ⅰ“, das vom Großbuchstaben „I“ getrennt ist. Sie sehen
üblicherweise gleich aus, aber es sind zwei verschiedene Zeichen mit
verschiedenen Bedeutungen.

Der Unicode-Standard beschreibt, wie Zeichen durch **Codepunkte**
dargestellt werden. Ein Codepunktwert ist eine Ganzzahl im Bereich von
0 bis 0x10FFFF (etwa 1,1 Millionen Werte – die tatsächlich zugewiesene
Anzahl ist geringer). Im Standard und in diesem Dokument wird ein
Codepunkt in der Schreibweise "U+265E" notiert, womit das Zeichen mit
dem Wert "0x265e" (dezimal 9822) gemeint ist.

Der Unicode-Standard enthält viele Tabellen, die Zeichen und ihre
zugehörigen Codepunkte auflisten:

   0061    'a'; LATIN SMALL LETTER A
   0062    'b'; LATIN SMALL LETTER B
   0063    'c'; LATIN SMALL LETTER C
   ...
   007B    '{'; LEFT CURLY BRACKET
   ...
   2167    'Ⅷ'; ROMAN NUMERAL EIGHT
   2168    'Ⅸ'; ROMAN NUMERAL NINE
   ...
   265E    '♞'; BLACK CHESS KNIGHT
   265F    '♟'; BLACK CHESS PAWN
   ...
   1F600   '😀'; GRINNING FACE
   1F609   '😉'; WINKING FACE
   ...

Streng genommen folgt aus diesen Begriffsbestimmungen, dass es sinnlos
ist zu sagen: „Das ist Zeichen "U+265E"“. "U+265E" ist ein Codepunkt,
der ein bestimmtes Zeichen darstellt. In diesem Fall stellt er das
Zeichen „BLACK CHESS KNIGHT“, „♞“, dar. In formlosen Zusammenhängen
wird dieser Unterschied zwischen Codepunkten und Zeichen manchmal
vergessen.

Ein Zeichen wird auf einem Bildschirm oder auf Papier durch eine Menge
grafischer Elemente dargestellt, die man **Glyphe** nennt. Die Glyphe
für ein großes A besteht zum Beispiel aus zwei diagonalen und einem
waagerechten Strich, wobei die genauen Einzelheiten von der
verwendeten Schriftart abhängen. Der meiste Python-Code muss sich um
Glyphen nicht kümmern. Die richtige Glyphe zur Anzeige herauszufinden
ist im Allgemeinen Aufgabe eines GUI-Werkzeugkastens oder des
Schriftzeichners eines Terminals.


Kodierungen
-----------

Um den vorherigen Abschnitt zusammenzufassen: Eine Unicode-
Zeichenkette ist eine Folge von Codepunkten, also von Zahlen von 0 bis
"0x10FFFF" (dezimal 1.114.111). Diese Folge von Codepunkten muss im
Speicher als Menge von **Codeeinheiten** dargestellt werden, und
**Codeeinheiten** werden dann auf 8-Bit-Bytes abgebildet. Die Regeln,
um eine Unicode-Zeichenkette in eine Folge von Bytes zu übersetzen,
nennt man **Zeichenkodierung** oder einfach **Kodierung**.

Die erste Kodierung, an die du denken könntest, verwendet 32-Bit-
Ganzzahlen als Codeeinheit und dann die Darstellung von 32-Bit-
Ganzzahlen durch die CPU. In dieser Darstellung könnte die
Zeichenkette „Python“ so aussehen:

      P           y           t           h           o           n
   0x50 00 00 00 79 00 00 00 74 00 00 00 68 00 00 00 6f 00 00 00 6e 00 00 00
      0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

Diese Darstellung ist einfach, bringt aber eine Reihe von Problemen
mit sich.

1. Sie ist nicht portabel. Verschiedene Prozessoren ordnen die Bytes
   unterschiedlich an.

2. Sie verschwendet sehr viel Platz. In den meisten Texten sind die
   meisten Codepunkte kleiner als 127 oder kleiner als 255, es wird
   also viel Platz von "0x00"-Bytes belegt. Die obige Zeichenkette
   braucht 24 Bytes gegenüber den 6 Bytes, die eine ASCII-Darstellung
   braucht. Der höhere Arbeitsspeicherverbrauch spielt keine große
   Rolle (Arbeitsplatzrechner haben Gigabytes an Arbeitsspeicher, und
   Zeichenketten sind üblicherweise nicht so groß), aber unseren
   Verbrauch an Festplattenplatz und Netzwerkbandbreite um den Faktor
   4 zu erhöhen ist nicht tragbar.

3. Sie ist nicht mit bestehenden C-Funktionen wie "strlen()"
   verträglich, es müsste also eine neue Familie von Funktionen für
   breite Zeichenketten verwendet werden.

Diese Kodierung wird daher nicht sehr viel verwendet, und man wählt
stattdessen andere Kodierungen, die effizienter und bequemer sind,
etwa UTF-8.

UTF-8 ist eine der am häufigsten verwendeten Kodierungen, und Python
verwendet sie oft standardmäßig. UTF steht für „Unicode Transformation
Format“, und die „8“ bedeutet, dass in der Kodierung 8-Bit-Werte
verwendet werden. (Es gibt auch die Kodierungen UTF-16 und UTF-32, die
aber seltener verwendet werden als UTF-8.) UTF-8 verwendet die
folgenden Regeln:

1. Ist der Codepunkt < 128, wird er durch den entsprechenden Bytewert
   dargestellt.

2. Ist der Codepunkt >= 128, wird er in eine Folge von zwei, drei oder
   vier Bytes verwandelt, wobei jedes Byte der Folge zwischen 128 und
   255 liegt.

UTF-8 hat mehrere bequeme Eigenschaften:

1. Es kann mit jedem Unicode-Codepunkt umgehen.

2. Eine Unicode-Zeichenkette wird in eine Folge von Bytes verwandelt,
   die eingebettete Nullbytes nur dort enthält, wo sie das Nullzeichen
   (U+0000) darstellen. Das heißt, dass UTF-8-Zeichenketten von
   C-Funktionen wie "strcpy()" verarbeitet und über Protokolle
   gesendet werden können, die Nullbytes nur als Endemarkierung einer
   Zeichenkette verkraften.

3. Eine Zeichenkette aus ASCII-Text ist auch gültiger UTF-8-Text.

4. UTF-8 ist recht kompakt. Die meisten häufig verwendeten Zeichen
   lassen sich mit einem oder zwei Bytes darstellen.

5. Werden Bytes beschädigt oder gehen verloren, lässt sich der Anfang
   des nächsten UTF-8-kodierten Codepunkts bestimmen und die
   Synchronisierung wiederherstellen. Es ist außerdem
   unwahrscheinlich, dass zufällige 8-Bit-Daten wie gültiges UTF-8
   aussehen.

6. UTF-8 ist eine byteorientierte Kodierung. Die Kodierung legt fest,
   dass jedes Zeichen durch eine bestimmte Folge von einem oder
   mehreren Bytes dargestellt wird. Das vermeidet die Probleme mit der
   Byte-Reihenfolge, die bei ganzzahl- und wortorientierten
   Kodierungen wie UTF-16 und UTF-32 auftreten können, bei denen die
   Folge der Bytes davon abhängt, auf welcher Hardware die
   Zeichenkette kodiert wurde.


Quellen
-------

Die Seite des Unicode-Konsortiums hat Zeichentabellen, ein Glossar und
PDF-Fassungen der Unicode-Spezifikation. Sei auf etwas schwierige
Lektüre vorbereitet. Auf der Seite gibt es außerdem eine Zeittafel zur
Entstehung und Entwicklung von Unicode.

Auf dem YouTube-Kanal Computerphile erörtert Tom Scott kurz die
Geschichte von Unicode und UTF-8 (9 Minuten 36 Sekunden).

Als Hilfe zum Verständnis des Standards hat Jukka Korpela eine
einführende Anleitung zum Lesen der Unicode-Zeichentabellen
geschrieben.

Ein weiterer guter einführender Artikel wurde von Joel Spolsky
geschrieben. Wenn diese Einführung dir die Dinge nicht klar gemacht
hat, solltest du diesen anderen Artikel lesen, bevor du weitermachst.

Wikipedia-Einträge sind oft hilfreich. Siehe zum Beispiel die Einträge
zu „character encoding“ und UTF-8.


Die Unicode-Unterstützung von Python
====================================

Nachdem du die Grundzüge von Unicode gelernt hast, können wir uns die
Unicode-Funktionen von Python ansehen.


Der Zeichenkettentyp
--------------------

Seit Python 3.0 enthält der Typ "str" der Sprache Unicode-Zeichen.
Jede Zeichenkette, die mit ""unicode rocks!"", "'unicode rocks!'" oder
der Syntax mit dreifachen Anführungszeichen erzeugt wird, ist also als
Unicode gespeichert.

Die Standardkodierung für Python-Quellcode ist UTF-8, du kannst ein
Unicode-Zeichen also einfach in ein Zeichenkettenliteral schreiben:

   try:
       with open('/tmp/input.txt', 'r') as f:
           ...
   except OSError:
       # 'File not found' error message.
       print("Fichier non trouvé")

Nebenbei: Python 3 unterstützt auch Unicode-Zeichen in Bezeichnern:

   répertoire = "/tmp/records.log"
   with open(répertoire, "w") as f:
       f.write("test\n")

Wenn du ein bestimmtes Zeichen in deinem Editor nicht eingeben kannst
oder den Quellcode aus irgendeinem Grund rein in ASCII halten willst,
kannst du in Zeichenkettenliteralen auch Escape-Sequenzen verwenden.
(Je nach System siehst du statt eines u-Escapes vielleicht die
tatsächliche Glyphe des großen Delta.)

   >>> "\N{GREEK CAPITAL LETTER DELTA}"  # Using the character name
   '\u0394'
   >>> "\u0394"                          # Using a 16-bit hex value
   '\u0394'
   >>> "\U00000394"                      # Using a 32-bit hex value
   '\u0394'

Außerdem lässt sich eine Zeichenkette über die Methode "decode()" von
"bytes" erzeugen. Diese Methode nimmt ein Argument *encoding*, etwa
"UTF-8", und wahlweise ein Argument *errors*.

Das Argument *errors* legt fest, was geschieht, wenn die
Eingabezeichenkette nicht nach den Regeln der Kodierung umgewandelt
werden kann. Zulässige Werte für dieses Argument sind "'strict'" (löst
eine "UnicodeDecodeError"-Ausnahme aus), "'replace'" (verwendet
"U+FFFD", "REPLACEMENT CHARACTER"), "'ignore'" (lässt das Zeichen im
Unicode-Ergebnis einfach weg) oder "'backslashreplace'" (fügt eine
"\xNN"-Escape-Sequenz ein). Die folgenden Beispiele zeigen die
Unterschiede:

   >>> b'\x80abc'.decode("utf-8", "strict")
   Traceback (most recent call last):
       ...
   UnicodeDecodeError: 'utf-8' codec can't decode byte 0x80 in position 0:
     invalid start byte
   >>> b'\x80abc'.decode("utf-8", "replace")
   '\ufffdabc'
   >>> b'\x80abc'.decode("utf-8", "backslashreplace")
   '\\x80abc'
   >>> b'\x80abc'.decode("utf-8", "ignore")
   'abc'

Kodierungen werden als Zeichenketten angegeben, die den Namen der
Kodierung enthalten. Python bringt rund 100 verschiedene Kodierungen
mit. Eine Liste steht in der Python-Bibliotheksreferenz unter Standard
Encodings. Manche Kodierungen haben mehrere Namen. So sind zum
Beispiel "'latin-1'", "'iso_8859_1'" und "'8859"' alle gleichbedeutend
mit derselben Kodierung.

Unicode-Zeichenketten mit einem Zeichen lassen sich auch mit der
integrierten Funktion "chr()" erzeugen, die Ganzzahlen nimmt und eine
Unicode-Zeichenkette der Länge 1 zurückgibt, die den entsprechenden
Codepunkt enthält. Der umgekehrte Vorgang ist die integrierte Funktion
"ord()", die eine Unicode-Zeichenkette mit einem Zeichen nimmt und den
Codepunktwert zurückgibt:

   >>> chr(57344)
   '\ue000'
   >>> ord('\ue000')
   57344


In Bytes umwandeln
------------------

Das Gegenstück zu "bytes.decode()" ist "str.encode()", das eine
"bytes"-Darstellung der Unicode-Zeichenkette zurückgibt, kodiert in
der gewünschten *encoding*.

Der Parameter *errors* ist derselbe wie der Parameter der Methode
"decode()", unterstützt aber ein paar weitere mögliche Behandlungen.
Neben "'strict'", "'ignore'" und "'replace'" (das in diesem Fall ein
Fragezeichen anstelle des nicht kodierbaren Zeichens einfügt) gibt es
außerdem "'xmlcharrefreplace'" (fügt eine XML-Zeichenreferenz ein),
"backslashreplace" (fügt eine "\uNNNN"-Escape-Sequenz ein) und
"namereplace" (fügt eine "\N{...}"-Escape-Sequenz ein).

Das folgende Beispiel zeigt die unterschiedlichen Ergebnisse:

   >>> u = chr(40960) + 'abcd' + chr(1972)
   >>> u.encode('utf-8')
   b'\xea\x80\x80abcd\xde\xb4'
   >>> u.encode('ascii')
   Traceback (most recent call last):
       ...
   UnicodeEncodeError: 'ascii' codec can't encode character '\ua000' in
     position 0: ordinal not in range(128)
   >>> u.encode('ascii', 'ignore')
   b'abcd'
   >>> u.encode('ascii', 'replace')
   b'?abcd?'
   >>> u.encode('ascii', 'xmlcharrefreplace')
   b'&#40960;abcd&#1972;'
   >>> u.encode('ascii', 'backslashreplace')
   b'\\ua000abcd\\u07b4'
   >>> u.encode('ascii', 'namereplace')
   b'\\N{YI SYLLABLE IT}abcd\\u07b4'

Die tiefliegenden Routinen zum Registrieren und Verwenden der
verfügbaren Kodierungen finden sich im Modul "codecs". Neue
Kodierungen umzusetzen erfordert ebenfalls ein Verständnis des Moduls
"codecs". Allerdings liegen die Kodier- und Dekodierfunktionen, die
dieses Modul zurückgibt, üblicherweise tiefer als bequem ist, und neue
Kodierungen zu schreiben ist eine Spezialaufgabe. Das Modul wird in
diesem HOWTO daher nicht behandelt.


Unicode-Literale im Python-Quellcode
------------------------------------

Im Python-Quellcode lassen sich bestimmte Unicode-Codepunkte mit der
Escape-Sequenz "\u" schreiben, auf die vier Hexadezimalziffern folgen,
die den Codepunkt angeben. Die Escape-Sequenz "\U" ist ähnlich,
erwartet aber acht Hexadezimalziffern und nicht vier:

   >>> s = "a\xac\u1234\u20ac\U00008000"
   ... #     ^^^^ two-digit hex escape
   ... #         ^^^^^^ four-digit Unicode escape
   ... #                     ^^^^^^^^^^ eight-digit Unicode escape
   >>> [ord(c) for c in s]
   [97, 172, 4660, 8364, 32768]

Escape-Sequenzen für Codepunkte größer als 127 sind in kleinen Dosen
in Ordnung, werden aber lästig, wenn du viele Zeichen mit Akzenten
verwendest – wie in einem Programm mit Meldungen auf Französisch oder
in einer anderen Sprache mit Akzenten. Du kannst Zeichenketten auch
mit der integrierten Funktion "chr()" zusammensetzen, aber das ist
noch mühsamer.

Im Idealfall willst du Literale in der natürlichen Kodierung deiner
Sprache schreiben können. Du könntest den Python-Quellcode dann mit
deinem Lieblingseditor bearbeiten, der die Zeichen mit Akzenten
natürlich anzeigt, und zur Laufzeit würden die richtigen Zeichen
verwendet.

Python unterstützt standardmäßig das Schreiben von Quellcode in UTF-8,
aber du kannst fast jede Kodierung verwenden, wenn du die verwendete
Kodierung angibst. Das geschieht über einen besonderen Kommentar in
der ersten oder zweiten Zeile der Quelldatei:

   #!/usr/bin/env python
   # -*- coding: latin-1 -*-

   u = 'abcdé'
   print(ord(u[-1]))

Die Syntax ist von der Notation in Emacs zur Angabe dateilokaler
Variablen angeregt. Emacs unterstützt viele verschiedene Variablen,
Python aber nur „coding“. Die Zeichen "-*-" zeigen Emacs an, dass der
Kommentar besonders ist. Für Python haben sie keine Bedeutung, sind
aber Konvention. Python sucht im Kommentar nach "coding: name" oder
"coding=name".

Wenn du keinen solchen Kommentar einfügst, wird wie schon erwähnt
UTF-8 als Standardkodierung verwendet. Weitere Informationen stehen
auch in **PEP 263**.


Unicode-Eigenschaften
---------------------

Die Unicode-Spezifikation enthält eine Datenbank mit Informationen
über Codepunkte. Für jeden festgelegten Codepunkt umfassen die
Informationen den Namen des Zeichens, seine Kategorie und, wo
anwendbar, den Zahlenwert (bei Zeichen, die Zahlbegriffe darstellen,
etwa die römischen Ziffern oder Brüche wie ein Drittel und vier
Fünftel). Es gibt außerdem Eigenschaften zur Darstellung, etwa wie der
Codepunkt in bidirektionalem Text zu verwenden ist.

Das folgende Programm zeigt einige Informationen über mehrere Zeichen
und gibt den Zahlenwert eines bestimmten Zeichens aus:

   import unicodedata

   u = chr(233) + chr(0x0bf2) + chr(3972) + chr(6000) + chr(13231)

   for i, c in enumerate(u):
       print(i, '%04x' % ord(c), unicodedata.category(c), end=" ")
       print(unicodedata.name(c))

   # Get numeric value of second character
   print(unicodedata.numeric(u[1]))

Ausgeführt gibt das Folgendes aus:

   0 00e9 Ll LATIN SMALL LETTER E WITH ACUTE
   1 0bf2 No TAMIL NUMBER ONE THOUSAND
   2 0f84 Mn TIBETAN MARK HALANTA
   3 1770 Lo TAGBANWA LETTER SA
   4 33af So SQUARE RAD OVER S SQUARED
   1000.0

Die Kategoriecodes sind Abkürzungen, die die Art des Zeichens
beschreiben. Sie sind in Kategorien wie „Letter“, „Number“,
„Punctuation“ oder „Symbol“ gruppiert, die wiederum in Unterkategorien
aufgeteilt sind. Nehmen wir die Codes aus der obigen Ausgabe: "'Ll'"
bedeutet „Letter, lowercase“, "'No'" bedeutet „Number, other“, "'Mn'"
ist „Mark, nonspacing“ und "'So'" ist „Symbol, other“. Eine Liste der
Kategoriecodes steht im Abschnitt General Category Values der
Dokumentation zur Unicode Character Database.


Zeichenketten vergleichen
-------------------------

Unicode macht den Vergleich von Zeichenketten etwas komplizierter,
weil dieselbe Zeichenmenge durch unterschiedliche Folgen von
Codepunkten dargestellt werden kann. So lässt sich ein Buchstabe wie
„ê“ als einzelner Codepunkt U+00EA darstellen oder als U+0065 U+0302,
also als Codepunkt für „e“ gefolgt von einem Codepunkt für „COMBINING
CIRCUMFLEX ACCENT“. Ausgegeben erzeugen sie dieselbe Ausgabe, aber
eine Zeichenkette hat die Länge 1 und die andere die Länge 2.

Ein Werkzeug für einen Vergleich ohne Beachtung der Groß- und
Kleinschreibung ist die Zeichenkettenmethode "casefold()", die eine
Zeichenkette nach einem im Unicode-Standard beschriebenen Algorithmus
in eine Form ohne Groß- und Kleinschreibung umwandelt. Dieser
Algorithmus behandelt Zeichen wie den deutschen Buchstaben „ß“
(Codepunkt U+00DF) besonders: Daraus wird das Kleinbuchstabenpaar
„ss“.

   >>> street = 'Gürzenichstraße'
   >>> street.casefold()
   'gürzenichstrasse'

Ein zweites Werkzeug ist die Funktion "normalize()" des Moduls
"unicodedata", die Zeichenketten in eine von mehreren Normalformen
umwandelt, in denen Buchstaben, auf die ein kombinierendes Zeichen
folgt, durch einzelne Zeichen ersetzt werden. "normalize()" lässt sich
verwenden, um Zeichenketten zu vergleichen, ohne fälschlich
Ungleichheit zu melden, wenn zwei Zeichenketten kombinierende Zeichen
unterschiedlich verwenden:

   import unicodedata

   def compare_strs(s1, s2):
       def NFD(s):
           return unicodedata.normalize('NFD', s)

       return NFD(s1) == NFD(s2)

   single_char = 'ê'
   multiple_chars = '\N{LATIN SMALL LETTER E}\N{COMBINING CIRCUMFLEX ACCENT}'
   print('length of first string=', len(single_char))
   print('length of second string=', len(multiple_chars))
   print(compare_strs(single_char, multiple_chars))

Ausgeführt gibt das Folgendes aus:

   $ python compare-strs.py
   length of first string= 1
   length of second string= 2
   True

Das erste Argument der Funktion "normalize()" ist eine Zeichenkette,
die die gewünschte Normalform angibt. Sie kann „NFC“, „NFKC“, „NFD“
oder „NFKD“ sein.

Der Unicode-Standard legt außerdem fest, wie Vergleiche ohne Beachtung
der Groß- und Kleinschreibung durchzuführen sind:

   import unicodedata

   def compare_caseless(s1, s2):
       def NFD(s):
           return unicodedata.normalize('NFD', s)

       return NFD(NFD(s1).casefold()) == NFD(NFD(s2).casefold())

   # Example usage
   single_char = 'ê'
   multiple_chars = '\N{LATIN CAPITAL LETTER E}\N{COMBINING CIRCUMFLEX ACCENT}'

   print(compare_caseless(single_char, multiple_chars))

Das gibt "True" aus. (Warum wird "NFD()" zweimal aufgerufen? Weil es
ein paar Zeichen gibt, bei denen "casefold()" eine nicht normalisierte
Zeichenkette zurückgibt. Das Ergebnis muss daher erneut normalisiert
werden. Eine Erörterung und ein Beispiel stehen in Abschnitt 3.13 des
Unicode-Standards.)


Unicode-reguläre-Ausdrücke
--------------------------

Die regulären Ausdrücke, die das Modul "re" unterstützt, lassen sich
entweder als Bytes oder als Zeichenketten übergeben. Einige der
besonderen Zeichenfolgen wie "\d" und "\w" haben unterschiedliche
Bedeutungen, je nachdem, ob das Muster als Bytes oder als Zeichenkette
übergeben wird. So passt "\d" bei Bytes auf die Zeichen "[0-9]", bei
Zeichenketten aber auf jedes Zeichen, das in der Kategorie "'Nd'"
liegt.

Die Zeichenkette in diesem Beispiel enthält die Zahl 57, geschrieben
sowohl in thailändischen als auch in arabischen Ziffern:

   import re
   p = re.compile(r'\d+')

   s = "Over \u0e55\u0e57 57 flavours"
   m = p.search(s)
   print(repr(m.group()))

Ausgeführt passt "\d+" auf die thailändischen Ziffern und gibt sie
aus. Übergibst du "compile()" das Flag "re.ASCII", passt "\d+"
stattdessen auf die Teilzeichenkette „57“.

Ebenso passt "\w" auf eine große Vielfalt von Unicode-Zeichen, bei
Bytes oder bei gesetztem "re.ASCII" aber nur auf "[a-zA-Z0-9_]", und
"\s" passt entweder auf Unicode-Leerraumzeichen oder auf "[
\t\n\r\f\v]".


Quellen
-------

Einige gute weitere Erörterungen der Unicode-Unterstützung von Python
sind:

* Processing Text Files in Python 3 von Nick Coghlan.

* Pragmatic Unicode, ein Vortrag von Ned Batchelder auf der PyCon
  2012.

Der Typ "str" ist in der Python-Bibliotheksreferenz unter
Textsequenztyp – str beschrieben.

Die Dokumentation des Moduls "unicodedata".

Die Dokumentation des Moduls "codecs".

Marc-André Lemburg hielt auf der EuroPython 2002 einen Vortrag mit dem
Titel „Python and Unicode“ (PDF-Folien). Die Folien sind ein
ausgezeichneter Überblick über den Entwurf der Unicode-Funktionen von
Python 2 (wo der Unicode-Zeichenkettentyp "unicode" heißt und Literale
mit "u" beginnen).


Unicode-Daten lesen und schreiben
=================================

Sobald du etwas Code geschrieben hast, der mit Unicode-Daten arbeitet,
ist das nächste Problem die Ein- und Ausgabe. Wie bekommst du Unicode-
Zeichenketten in dein Programm, und wie wandelst du Unicode in eine
Form um, die sich zum Speichern oder Übertragen eignet?

Möglicherweise musst du je nach deinen Eingabequellen und
Ausgabezielen gar nichts tun. Du solltest prüfen, ob die in deiner
Anwendung verwendeten Bibliotheken Unicode von sich aus unterstützen.
XML-Parser geben zum Beispiel oft Unicode-Daten zurück. Viele
relationale Datenbanken unterstützen ebenfalls Spalten mit Unicode-
Werten und können Unicode-Werte aus einer SQL-Abfrage zurückgeben.

Unicode-Daten werden üblicherweise in eine bestimmte Kodierung
umgewandelt, bevor sie auf die Festplatte geschrieben oder über einen
Socket gesendet werden. Es ist möglich, die ganze Arbeit selbst zu
machen: eine Datei öffnen, ein 8-Bit-bytes-Objekt daraus lesen und die
Bytes mit "bytes.decode(encoding)" umwandeln. Dieser Weg von Hand ist
allerdings nicht empfehlenswert.

Ein Problem ist die Mehrbyte-Natur von Kodierungen. Ein Unicode-
Zeichen kann durch mehrere Bytes dargestellt werden. Wenn du die Datei
in Blöcken beliebiger Größe lesen willst (sagen wir 1024 oder 4096
Bytes), musst du Code zur Fehlerbehandlung schreiben, der den Fall
abfängt, dass am Ende eines Blocks nur ein Teil der Bytes gelesen
wird, die ein einzelnes Unicode-Zeichen kodieren. Eine Lösung wäre,
die ganze Datei in den Speicher zu lesen und dann zu dekodieren, aber
das verhindert die Arbeit mit extrem großen Dateien. Musst du eine 2
-GiB-Datei lesen, brauchst du 2 GiB Arbeitsspeicher. (Eigentlich mehr,
weil du zumindest kurzzeitig sowohl die kodierte Zeichenkette als auch
ihre Unicode-Fassung im Speicher hättest.)

Die Lösung wäre, die tiefliegende Dekodierschnittstelle zu verwenden,
um den Fall unvollständiger Kodierfolgen abzufangen. Diese Arbeit ist
schon für dich erledigt: Die integrierte Funktion "open()" kann ein
dateiartiges Objekt zurückgeben, das davon ausgeht, dass der Inhalt
der Datei in einer angegebenen Kodierung vorliegt, und das für
Methoden wie "read()" und "write()" Unicode-Parameter annimmt. Das
funktioniert über die Parameter *encoding* und *errors* von "open()",
die genauso ausgelegt werden wie die in "str.encode()" und
"bytes.decode()".

Unicode aus einer Datei zu lesen ist daher einfach:

   with open('unicode.txt', encoding='utf-8') as f:
       for line in f:
           print(repr(line))

Es ist auch möglich, Dateien im Aktualisierungsmodus zu öffnen und
damit sowohl zu lesen als auch zu schreiben:

   with open('test', encoding='utf-8', mode='w+') as f:
       f.write('\u4500 blah blah blah\n')
       f.seek(0)
       print(repr(f.readline()[:1]))

Das Unicode-Zeichen "U+FEFF" wird als Byte-Reihenfolge-Markierung
(BOM) verwendet und oft als erstes Zeichen einer Datei geschrieben, um
die automatische Erkennung der Byte-Reihenfolge der Datei zu
unterstützen. Manche Kodierungen, etwa UTF-16, erwarten eine BOM am
Anfang einer Datei. Wird eine solche Kodierung verwendet, wird die BOM
automatisch als erstes Zeichen geschrieben und beim Lesen der Datei
stillschweigend verworfen. Es gibt Varianten dieser Kodierungen, etwa
„utf-16-le“ und „utf-16-be“ für Little-Endian- und Big-Endian-
Kodierungen, die eine bestimmte Byte-Reihenfolge festlegen und die BOM
nicht überspringen.

In manchen Bereichen ist es außerdem Konvention, am Anfang
UTF-8-kodierter Dateien eine „BOM“ zu verwenden. Der Name führt in die
Irre, weil UTF-8 nicht von der Byte-Reihenfolge abhängt. Die
Markierung kündigt lediglich an, dass die Datei in UTF-8 kodiert ist.
Um solche Dateien zu lesen, verwende den Codec „utf-8-sig“, der die
Markierung, falls vorhanden, automatisch überspringt.


Unicode-Dateinamen
------------------

Die meisten heute verbreiteten Betriebssysteme unterstützen
Dateinamen, die beliebige Unicode-Zeichen enthalten. Üblicherweise
wird das umgesetzt, indem die Unicode-Zeichenkette in eine Kodierung
umgewandelt wird, die je nach System unterschiedlich ist. Heute nähert
sich Python der Verwendung von UTF-8 an: Python unter macOS verwendet
seit mehreren Versionen UTF-8, und Python 3.6 ist auch unter Windows
auf UTF-8 umgestiegen. Auf Unix-Systemen gibt es nur dann eine
*Dateisystem-Kodierung*, wenn du die Umgebungsvariablen "LANG" oder
"LC_CTYPE" gesetzt hast, wenn nicht, ist die Standardkodierung wieder
UTF-8.

Die Funktion "sys.getfilesystemencoding()" gibt die Kodierung zurück,
die auf deinem aktuellen System zu verwenden ist, falls du von Hand
kodieren willst – es gibt aber nicht viel Grund, sich die Mühe zu
machen. Wenn du eine Datei zum Lesen oder Schreiben öffnest, kannst du
üblicherweise einfach die Unicode-Zeichenkette als Dateinamen
übergeben, und sie wird automatisch in die richtige Kodierung
umgewandelt:

   filename = 'filename\u4500abc'
   with open(filename, 'w') as f:
       f.write('blah\n')

Funktionen im Modul "os" wie "os.stat()" nehmen ebenfalls Unicode-
Dateinamen an.

Die Funktion "os.listdir()" gibt Dateinamen zurück, was eine Frage
aufwirft: Soll sie die Unicode-Fassung der Dateinamen zurückgeben oder
Bytes, die die kodierten Fassungen enthalten? "os.listdir()" kann
beides, je nachdem, ob du den Verzeichnispfad als Bytes oder als
Unicode-Zeichenkette übergeben hast. Übergibst du eine Unicode-
Zeichenkette als Pfad, werden die Dateinamen mit der Kodierung des
Dateisystems dekodiert und eine Liste von Unicode-Zeichenketten
zurückgegeben. Übergibst du einen Bytepfad, kommen die Dateinamen als
Bytes zurück. Nehmen wir zum Beispiel an, die Standard-*Dateisystem-
Kodierung* ist UTF-8. Dann erzeugt das folgende Programm:

   fn = 'filename\u4500abc'
   f = open(fn, 'w')
   f.close()

   import os
   print(os.listdir(b'.'))
   print(os.listdir('.'))

die folgende Ausgabe:

   $ python listdir-test.py
   [b'filename\xe4\x94\x80abc', ...]
   ['filename\u4500abc', ...]

Die erste Liste enthält UTF-8-kodierte Dateinamen, die zweite Liste
die Unicode-Fassungen.

Beachte, dass du bei diesen APIs in den meisten Fällen einfach bei
Unicode bleiben kannst. Die byte-basierten APIs sollten nur auf
Systemen verwendet werden, auf denen nicht dekodierbare Dateinamen
vorkommen können. Das sind heute so gut wie nur noch Unix-Systeme.


Hinweise zum Schreiben Unicode-fähiger Programme
------------------------------------------------

Dieser Abschnitt gibt einige Vorschläge zum Schreiben von Software,
die mit Unicode umgeht.

Der wichtigste Hinweis ist:

   Software sollte intern nur mit Unicode-Zeichenketten arbeiten, die
   Eingabedaten so früh wie möglich dekodieren und die Ausgabe erst am
   Ende kodieren.

Wenn du versuchst, Verarbeitungsfunktionen zu schreiben, die sowohl
Unicode- als auch Byte-Zeichenketten annehmen, wird dein Programm
überall dort fehleranfällig, wo du die beiden verschiedenen Arten von
Zeichenketten verbindest. Es gibt kein automatisches Kodieren oder
Dekodieren: Machst du zum Beispiel "str + bytes", wird ein "TypeError"
ausgelöst.

Bei Daten, die von einem Webbrowser oder einer anderen nicht
vertrauenswürdigen Quelle kommen, ist es eine verbreitete Technik,
eine Zeichenkette auf unzulässige Zeichen zu prüfen, bevor sie in
einer erzeugten Befehlszeile verwendet oder in einer Datenbank
gespeichert wird. Wenn du das tust, achte darauf, die dekodierte
Zeichenkette zu prüfen und nicht die kodierten Bytedaten. Manche
Kodierungen haben interessante Eigenschaften, etwa nicht bijektiv oder
nicht vollständig ASCII-verträglich zu sein. Das gilt besonders dann,
wenn die Eingabedaten auch die Kodierung angeben, weil der Angreifer
dann einen raffinierten Weg wählen kann, bösartigen Text im kodierten
Bytestrom zu verstecken.


Zwischen Dateikodierungen umwandeln
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Die Klasse "StreamRecoder" kann transparent zwischen Kodierungen
umwandeln: Sie nimmt einen Strom, der Daten in Kodierung 1 liefert,
und verhält sich wie ein Strom, der Daten in Kodierung 2 liefert.

Wenn du zum Beispiel eine Eingabedatei *f* in Latin-1 hast, kannst du
sie mit einem "StreamRecoder" umhüllen, damit UTF-8-kodierte Bytes
zurückkommen:

   new_f = codecs.StreamRecoder(f,
       # en/decoder: used by read() to encode its results and
       # by write() to decode its input.
       codecs.getencoder('utf-8'), codecs.getdecoder('utf-8'),

       # reader/writer: used to read and write to the stream.
       codecs.getreader('latin-1'), codecs.getwriter('latin-1') )


Dateien in unbekannter Kodierung
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Was kannst du tun, wenn du eine Datei ändern musst, aber deren
Kodierung nicht kennst? Wenn du weißt, dass die Kodierung ASCII-
verträglich ist, und nur die ASCII-Teile untersuchen oder verändern
willst, kannst du die Datei mit der Fehlerbehandlung "surrogateescape"
öffnen:

   with open(fname, 'r', encoding="ascii", errors="surrogateescape") as f:
       data = f.read()

   # make changes to the string 'data'

   with open(fname + '.new', 'w',
             encoding="ascii", errors="surrogateescape") as f:
       f.write(data)

Die Fehlerbehandlung "surrogateescape" dekodiert alle Nicht-ASCII-
Bytes als Codepunkte in einem besonderen Bereich von U+DC80 bis
U+DCFF. Diese Codepunkte werden wieder zu denselben Bytes, wenn die
Fehlerbehandlung "surrogateescape" zum Kodieren der Daten und zum
Zurückschreiben verwendet wird.


Quellen
-------

Ein Abschnitt von Mastering Python 3 Input/Output, einem Vortrag von
David Beazley auf der PyCon 2010, behandelt Textverarbeitung und den
Umgang mit Binärdaten.

Die PDF-Folien zu Marc-André Lemburgs Vortrag „Writing Unicode-aware
Applications in Python“ behandeln Fragen der Zeichenkodierung sowie
die Internationalisierung und Lokalisierung einer Anwendung. Diese
Folien behandeln nur Python 2.x.

The Guts of Unicode in Python ist ein Vortrag von Benjamin Peterson
auf der PyCon 2013, der die interne Unicode-Darstellung in Python 3.3
behandelt.


Danksagungen
============

Der erste Entwurf dieses Dokuments wurde von Andrew Kuchling
geschrieben. Seither wurde es von Alexander Belopolsky, Georg Brandl,
Andrew Kuchling und Ezio Melotti weiter überarbeitet.

Dank an die folgenden Personen, die Fehler bemerkt oder Vorschläge zu
diesem Artikel gemacht haben: Éric Araujo, Nicholas Bastin, Nick
Coghlan, Marius Gedminas, Kent Johnson, Ken Krugler, Marc-André
Lemburg, Martin von Löwis, Terry J. Reedy, Serhiy Storchaka, Eryk Sun,
Chad Whitacre, Graham Wideman.
