Ich denke, wir können dank der Kleinigkeiten aus verschiedenen Antworten eine Erklärung zusammenstellen.
Beim Versuch, eine Unicode-Zeichenfolge u '\ xe9' zu drucken, versucht Python implizit, diese Zeichenfolge mit dem derzeit in sys.stdout.encoding gespeicherten Codierungsschema zu codieren. Python übernimmt diese Einstellung tatsächlich aus der Umgebung, aus der es initiiert wurde. Wenn keine ordnungsgemäße Codierung aus der Umgebung gefunden werden kann, wird erst dann die Standardcodierung ASCII wiederhergestellt.
Zum Beispiel verwende ich eine Bash-Shell, deren Codierung standardmäßig UTF-8 ist. Wenn ich Python davon starte, nimmt es diese Einstellung auf und verwendet sie:
$ python
>>> import sys
>>> print sys.stdout.encoding
UTF-8
Lassen Sie uns für einen Moment die Python-Shell verlassen und die Bash-Umgebung mit einer falschen Codierung festlegen:
$ export LC_CTYPE=klingon
# we should get some error message here, just ignore it.
Starten Sie dann die Python-Shell erneut und stellen Sie sicher, dass sie tatsächlich zu ihrer Standard-ASCII-Codierung zurückkehrt.
$ python
>>> import sys
>>> print sys.stdout.encoding
ANSI_X3.4-1968
Bingo!
Wenn Sie jetzt versuchen, ein Unicode-Zeichen außerhalb von ASCII auszugeben, sollten Sie eine nette Fehlermeldung erhalten
>>> print u'\xe9'
UnicodeEncodeError: 'ascii' codec can't encode character u'\xe9'
in position 0: ordinal not in range(128)
Beenden wir Python und verwerfen die Bash-Shell.
Wir werden nun beobachten, was passiert, nachdem Python Strings ausgegeben hat. Dazu starten wir zuerst eine Bash-Shell in einem Grafikterminal (ich verwende Gnome Terminal) und stellen das Terminal so ein, dass die Ausgabe mit ISO-8859-1 (auch bekannt als Latin-1) dekodiert wird (Grafikterminals haben normalerweise die Option, Zeichen zu setzen Codierung in einem ihrer Dropdown-Menüs). Beachten Sie, dass dies die Codierung der tatsächlichen Shell-Umgebung nicht ändert , sondern nur die Art und Weise, wie das Terminal selbst die ausgegebene Ausgabe dekodiert, ähnlich wie dies bei einem Webbrowser der Fall ist. Sie können daher die Codierung des Terminals unabhängig von der Shell-Umgebung ändern. Starten wir dann Python von der Shell aus und stellen Sie sicher, dass sys.stdout.encoding auf die Codierung der Shell-Umgebung eingestellt ist (UTF-8 für mich):
$ python
>>> import sys
>>> print sys.stdout.encoding
UTF-8
>>> print '\xe9' # (1)
é
>>> print u'\xe9' # (2)
é
>>> print u'\xe9'.encode('latin-1') # (3)
é
>>>
(1) Python gibt die Binärzeichenfolge unverändert aus, das Terminal empfängt sie und versucht, ihren Wert mit der Latin-1-Zeichenzuordnung abzugleichen. In Latin-1 ergibt 0xe9 oder 233 das Zeichen "é", und so wird das Terminal angezeigt.
(2) Python versucht, die Unicode-Zeichenfolge implizit mit dem derzeit in sys.stdout.encoding festgelegten Schema zu codieren. In diesem Fall handelt es sich um "UTF-8". Nach der UTF-8-Codierung lautet die resultierende Binärzeichenfolge '\ xc3 \ xa9' (siehe spätere Erläuterung). Das Terminal empfängt den Stream als solchen und versucht, 0xc3a9 mit Latin-1 zu decodieren, aber Latin-1 geht von 0 auf 255 und decodiert daher jeweils nur 1 Byte Streams. 0xc3a9 ist 2 Bytes lang, der Latin-1-Decoder interpretiert es daher als 0xc3 (195) und 0xa9 (169) und ergibt 2 Zeichen: Ã und ©.
(3) Python codiert den Unicode-Codepunkt u '\ xe9' (233) mit dem Latin-1-Schema. Es stellt sich heraus, dass der Bereich für Latin-1-Codepunkte zwischen 0 und 255 liegt und auf genau dasselbe Zeichen wie Unicode in diesem Bereich verweist. Daher ergeben Unicode-Codepunkte in diesem Bereich den gleichen Wert, wenn sie in Latin-1 codiert werden. U '\ xe9' (233), das in Latin-1 codiert ist, ergibt also auch die Binärzeichenfolge '\ xe9'. Das Terminal empfängt diesen Wert und versucht, ihn auf der Latin-1-Zeichenkarte abzugleichen. Genau wie in Fall (1) ergibt es "é" und das wird angezeigt.
Lassen Sie uns nun die Codierungseinstellungen des Terminals aus dem Dropdown-Menü in UTF-8 ändern (so wie Sie die Codierungseinstellungen Ihres Webbrowsers ändern würden). Sie müssen Python nicht stoppen oder die Shell neu starten. Die Codierung des Terminals stimmt jetzt mit der von Python überein. Versuchen wir es noch einmal:
>>> print '\xe9' # (4)
>>> print u'\xe9' # (5)
é
>>> print u'\xe9'.encode('latin-1') # (6)
>>>
(4) Python gibt eine Binärzeichenfolge unverändert aus. Das Terminal versucht, diesen Stream mit UTF-8 zu dekodieren. UTF-8 versteht den Wert 0xe9 jedoch nicht (siehe spätere Erklärung) und kann ihn daher nicht in einen Unicode-Codepunkt konvertieren. Kein Codepunkt gefunden, kein Zeichen gedruckt.
(5) Python versucht, die Unicode-Zeichenfolge implizit mit den Angaben in sys.stdout.encoding zu codieren. Immer noch "UTF-8". Die resultierende Binärzeichenfolge lautet '\ xc3 \ xa9'. Das Terminal empfängt den Stream und versucht, 0xc3a9 auch mit UTF-8 zu dekodieren. Es ergibt den Rückcodewert 0xe9 (233), der auf der Unicode-Zeichenkarte auf das Symbol "é" zeigt. Terminal zeigt "é" an.
(6) Python codiert eine Unicode-Zeichenfolge mit Latin-1 und liefert eine Binärzeichenfolge mit demselben Wert '\ xe9'. Auch für das Terminal ist dies so ziemlich das Gleiche wie in Fall (4).
Schlussfolgerungen: - Python gibt Nicht-Unicode-Zeichenfolgen als Rohdaten aus, ohne die Standardcodierung zu berücksichtigen. Das Terminal zeigt sie nur an, wenn die aktuelle Codierung mit den Daten übereinstimmt. - Python gibt Unicode-Zeichenfolgen aus, nachdem sie mit dem in sys.stdout.encoding angegebenen Schema codiert wurden. - Python erhält diese Einstellung aus der Umgebung der Shell. - Das Terminal zeigt die Ausgabe gemäß seinen eigenen Codierungseinstellungen an. - Die Codierung des Terminals ist unabhängig von der der Shell.
Weitere Details zu Unicode, UTF-8 und Latin-1:
Unicode ist im Grunde eine Zeichentabelle, in der einige Schlüssel (Codepunkte) herkömmlicherweise zugewiesen wurden, um auf einige Symbole zu verweisen. zB durch Konvention wurde entschieden, dass Schlüssel 0xe9 (233) der Wert ist, der auf das Symbol 'é' zeigt. ASCII und Unicode verwenden dieselben Codepunkte von 0 bis 127 wie Latin-1 und Unicode von 0 bis 255. Das heißt, 0x41 zeigt auf 'A' in ASCII, Latin-1 und Unicode, 0xc8 zeigt auf 'Ü' in Latin-1 und Unicode, 0xe9 zeigt in Latin-1 und Unicode auf 'é'.
Bei der Arbeit mit elektronischen Geräten benötigen Unicode-Codepunkte eine effiziente Möglichkeit, elektronisch dargestellt zu werden. Darum geht es bei Codierungsschemata. Es gibt verschiedene Unicode-Codierungsschemata (utf7, UTF-8, UTF-16, UTF-32). Der intuitivste und einfachste Codierungsansatz wäre, einfach den Wert eines Codepunkts in der Unicode-Karte als Wert für seine elektronische Form zu verwenden. Unicode verfügt derzeit jedoch über mehr als eine Million Codepunkte, was bedeutet, dass einige von ihnen 3 Bytes benötigen ausgedrückt. Um effizient mit Text arbeiten zu können, wäre eine 1: 1-Zuordnung eher unpraktisch, da alle Codepunkte unabhängig von ihrem tatsächlichen Bedarf auf genau demselben Speicherplatz mit mindestens 3 Bytes pro Zeichen gespeichert werden müssten.
Die meisten Codierungsschemata weisen Mängel hinsichtlich des Platzbedarfs auf, die wirtschaftlichsten decken nicht alle Unicode-Codepunkte ab, zum Beispiel deckt ASCII nur die ersten 128 ab, während Latin-1 die ersten 256 abdeckt. Andere, die versuchen, umfassender zu sein, enden ebenfalls verschwenderisch sein, da sie mehr Bytes als nötig benötigen, selbst für übliche "billige" Zeichen. UTF-16 verwendet beispielsweise mindestens 2 Bytes pro Zeichen, einschließlich derjenigen im ASCII-Bereich ('B', das 65 ist, erfordert immer noch 2 Bytes Speicher in UTF-16). UTF-32 ist noch verschwenderischer, da alle Zeichen in 4 Bytes gespeichert werden.
UTF-8 hat das Dilemma mit einem Schema, das Codepunkte mit einer variablen Anzahl von Byte-Leerzeichen speichern kann, geschickt gelöst. Als Teil seiner Codierungsstrategie schnürt UTF-8 Codepunkte mit Flag-Bits, die (vermutlich für Decoder) ihren Platzbedarf und ihre Grenzen angeben.
UTF-8-Codierung von Unicode-Codepunkten im ASCII-Bereich (0-127):
0xxx xxxx (in binary)
- Die x zeigen den tatsächlichen Speicherplatz an, der zum "Speichern" des Codepunkts während der Codierung reserviert ist
- Die führende 0 ist ein Flag, das dem UTF-8-Decoder anzeigt, dass dieser Codepunkt nur 1 Byte benötigt.
- Beim Codieren ändert UTF-8 den Wert der Codepunkte in diesem bestimmten Bereich nicht (dh 65, die in UTF-8 codiert sind, sind ebenfalls 65). In Anbetracht der Tatsache, dass Unicode und ASCII auch in demselben Bereich kompatibel sind, sind UTF-8 und ASCII übrigens auch in diesem Bereich kompatibel.
Beispiel: Der Unicode-Codepunkt für 'B' ist '0x42' oder 0100 0010 in Binärform (wie gesagt, es ist dasselbe in ASCII). Nach der Codierung in UTF-8 wird es:
0xxx xxxx <-- UTF-8 encoding for Unicode code points 0 to 127
*100 0010 <-- Unicode code point 0x42
0100 0010 <-- UTF-8 encoded (exactly the same)
UTF-8-Codierung von Unicode-Codepunkten über 127 (nicht ASCII):
110x xxxx 10xx xxxx <-- (from 128 to 2047)
1110 xxxx 10xx xxxx 10xx xxxx <-- (from 2048 to 65535)
- Die führenden Bits '110' zeigen dem UTF-8-Decoder den Beginn eines in 2 Bytes codierten Codepunkts an, während '1110' 3 Bytes anzeigt, 11110 4 Bytes anzeigt und so weiter.
- Die inneren '10'-Flag-Bits werden verwendet, um den Beginn eines inneren Bytes zu signalisieren.
- Wiederum markieren die x den Bereich, in dem der Unicode-Codepunktwert nach der Codierung gespeichert wird.
Beispiel: 'é' Unicode-Codepunkt ist 0xe9 (233).
1110 1001 <-- 0xe9
Wenn UTF-8 diesen Wert codiert, wird festgestellt, dass der Wert größer als 127 und kleiner als 2048 ist. Daher sollte er in 2 Byte codiert werden:
110x xxxx 10xx xxxx <-- UTF-8 encoding for Unicode 128-2047
***0 0011 **10 1001 <-- 0xe9
1100 0011 1010 1001 <-- 'é' after UTF-8 encoding
C 3 A 9
Der 0xe9-Unicode-Code zeigt nach der UTF-8-Codierung auf 0xc3a9. Genau so empfängt das Terminal es. Wenn Ihr Terminal so eingestellt ist, dass Zeichenfolgen mit Latin-1 (einer der Nicht-Unicode-Legacy-Codierungen) dekodiert werden, wird à © angezeigt, da 0xc3 in Latin-1 nur auf à und 0xa9 auf © zeigt.