Der ganze Schlüssel zu solchen Codierung Problemen ist zu verstehen , dass es im Prinzip zwei verschiedenen Konzepte von „string“ : (1) Kette von Zeichen , und (2) string / Array von Bytes. Diese Unterscheidung wurde lange Zeit aufgrund der historischen Allgegenwart von Codierungen mit nicht mehr als 256 Zeichen (ASCII, Latin-1, Windows-1252, Mac OS Roman,…) weitgehend ignoriert: Diese Codierungen ordnen eine Reihe gemeinsamer Zeichen zu Zahlen zwischen 0 und 255 (dh Bytes); Der relativ begrenzte Austausch von Dateien vor dem Aufkommen des Webs machte diese Situation inkompatibler Codierungen erträglich, da die meisten Programme die Tatsache ignorieren konnten, dass es mehrere Codierungen gab, solange sie Text produzierten, der auf demselben Betriebssystem blieb: solche Programme würden es einfach tun Behandeln Sie Text als Bytes (durch die vom Betriebssystem verwendete Codierung). Die richtige, moderne Ansicht trennt diese beiden Zeichenfolgenkonzepte anhand der folgenden zwei Punkte ordnungsgemäß:
Zeichen haben meistens nichts mit Computern zu tun: Man kann sie auf eine Kreidetafel usw. zeichnen, wie zum Beispiel بايثون, 中 蟒 und 🐍. "Zeichen" für Maschinen enthalten auch "Zeichenanweisungen" wie z. B. Leerzeichen, Wagenrücklauf, Anweisungen zum Festlegen der Schreibrichtung (für Arabisch usw.), Akzente usw. Der Unicode- Standard enthält eine sehr große Zeichenliste . Es deckt die meisten bekannten Zeichen ab.
Auf der anderen Seite müssen Computer abstrakte Zeichen in irgendeiner Weise darstellen: Dazu verwenden sie Arrays von Bytes (einschließlich Zahlen zwischen 0 und 255), da ihr Speicher in Byte-Blöcken vorliegt. Der erforderliche Prozess, der Zeichen in Bytes konvertiert, wird als Codierung bezeichnet . Daher benötigt ein Computer eine Codierung, um Zeichen darzustellen. Jeder auf Ihrem Computer vorhandene Text wird codiert (bis er angezeigt wird), unabhängig davon, ob er an ein Terminal gesendet wird (das auf eine bestimmte Weise codierte Zeichen erwartet) oder in einer Datei gespeichert wird. Um angezeigt oder richtig "verstanden" zu werden (beispielsweise durch den Python-Interpreter), werden Byteströme in Zeichen dekodiert . Ein paar Kodierungen(UTF-8, UTF-16,…) werden von Unicode für seine Zeichenliste definiert (Unicode definiert somit sowohl eine Liste von Zeichen als auch Codierungen für diese Zeichen - es gibt immer noch Stellen, an denen der Ausdruck "Unicode-Codierung" als Möglichkeit, auf das allgegenwärtige UTF-8 zu verweisen, dies ist jedoch eine falsche Terminologie, da Unicode mehrere Codierungen bereitstellt .
Zusammenfassend muss gesagt werden, dass Computer Zeichen intern mit Bytes darstellen müssen , und zwar durch zwei Operationen:
Kodierung : Zeichen → Bytes
Dekodierung : Bytes → Zeichen
Einige Codierungen können nicht alle Zeichen (z. B. ASCII) codieren, während (einige) Unicode-Codierungen das Codieren aller Unicode-Zeichen ermöglichen. Die Codierung ist auch nicht unbedingt eindeutig , da einige Zeichen entweder direkt oder als Kombination dargestellt werden können (z. B. eines Basiszeichens und von Akzenten).
Beachten Sie, dass das Newline- Konzept eine zusätzliche Komplikationsebene darstellt , da es durch verschiedene (Steuer-) Zeichen dargestellt werden kann, die vom Betriebssystem abhängen (dies ist der Grund für den universellen Lesemodus für Newline-Dateien in Python ).
Was ich oben als "Zeichen" bezeichnet habe, nennt Unicode ein "vom Benutzer wahrgenommenes Zeichen ". Ein einzelnes vom Benutzer wahrgenommenes Zeichen kann manchmal in Unicode dargestellt werden, indem Zeichenteile (Basiszeichen, Akzente usw.) kombiniert werden, die an verschiedenen Indizes in der Unicode-Liste gefunden werden und als " Codepunkte " bezeichnet werden. Diese Codepunkte können zu einer Form kombiniert werden ein "Graphemcluster". Unicode führt somit zu einem dritten Konzept von Zeichenfolgen, das aus einer Folge von Unicode-Codepunkten besteht, die zwischen Byte- und Zeichenfolgen liegen und näher an letzteren liegen. Ich werde sie " Unicode-Strings " nennen (wie in Python 2).
Während Python Zeichenfolgen von (vom Benutzer wahrgenommenen) Zeichen drucken kann , sind Python-Nicht-Byte-Zeichenfolgen im Wesentlichen Sequenzen von Unicode-Codepunkten , nicht von vom Benutzer wahrgenommenen Zeichen. Die Codepunktwerte werden in der Python- \u
und \U
Unicode-Zeichenfolgensyntax verwendet. Sie sollten nicht mit der Codierung eines Zeichens verwechselt werden (und müssen keine Beziehung dazu haben: Unicode-Codepunkte können auf verschiedene Arten codiert werden).
Dies hat eine wichtige Konsequenz: die Länge eines Python (Unicode) string ist die Anzahl der Codepunkte, die ist nicht immer die Zahl der Benutzer wahrgenommenen Zeichen : also s = "\u1100\u1161\u11a8"; print(s, "len", len(s))
(Python 3) gibt 각 len 3
trotz s
einer einzigen benutzer wahrgenommen (Korean) mit Zeichen (weil es mit 3 Codepunkten dargestellt wird - auch wenn es nicht muss, wie print("\uac01")
zeigt). Unter vielen praktischen Umständen entspricht die Länge einer Zeichenfolge jedoch der Anzahl der vom Benutzer wahrgenommenen Zeichen, da viele Zeichen normalerweise von Python als einzelner Unicode-Codepunkt gespeichert werden.
In Python 2 werden Unicode-Zeichenfolgen als "Unicode-Zeichenfolgen" ( unicode
Typ, Literalform u"…"
) bezeichnet, während Byte-Arrays "Zeichenfolgen" sind ( str
Typ, bei dem das Array von Bytes beispielsweise mit Zeichenfolgenliteralen erstellt werden kann "…"
). In Python 3 werden Unicode-Zeichenfolgen einfach als "Zeichenfolgen" ( str
Typ, Literalform "…"
) bezeichnet, während Byte-Arrays "Bytes" ( bytes
Typ, Literalform b"…"
) sind. Infolgedessen "🐍"[0]
ergibt so etwas wie ein anderes Ergebnis in Python 2 ( '\xf0'
ein Byte) und Python 3 ( "🐍"
das erste und einzige Zeichen).
Mit diesen wenigen wichtigen Punkten sollten Sie in der Lage sein, die meisten Fragen im Zusammenhang mit der Codierung zu verstehen!
Normalerweise sollten Sie beim Drucken u"…"
auf einem Terminal keinen Müll bekommen: Python kennt die Codierung Ihres Terminals. Tatsächlich können Sie überprüfen, welche Codierung das Terminal erwartet:
% python
Python 2.7.6 (default, Nov 15 2013, 15:20:37)
[GCC 4.2.1 Compatible Apple LLVM 5.0 (clang-500.2.79)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> print sys.stdout.encoding
UTF-8
Wenn Ihre Eingabezeichen mit der Codierung des Terminals codiert werden können, wird Python dies tun und die entsprechenden Bytes an Ihr Terminal senden, ohne sich zu beschweren. Das Terminal bemüht sich dann, die Zeichen nach dem Decodieren der Eingabebytes anzuzeigen (im schlimmsten Fall enthält die Terminalschrift einige der Zeichen nicht und druckt stattdessen eine Art Leerzeichen).
Wenn Ihre Eingabezeichen nicht mit der Codierung des Terminals codiert werden können, bedeutet dies, dass das Terminal nicht für die Anzeige dieser Zeichen konfiguriert ist. Python wird sich beschweren (in Python mit einem, UnicodeEncodeError
da die Zeichenfolge nicht in einer Weise codiert werden kann, die zu Ihrem Terminal passt). Die einzig mögliche Lösung besteht darin, ein Terminal zu verwenden, das die Zeichen anzeigen kann (entweder indem Sie das Terminal so konfigurieren, dass es eine Codierung akzeptiert, die Ihre Zeichen darstellen kann, oder indem Sie ein anderes Terminalprogramm verwenden). Dies ist wichtig, wenn Sie Programme verteilen, die in verschiedenen Umgebungen verwendet werden können: Nachrichten, die Sie drucken, sollten im Terminal des Benutzers darstellbar sein. Manchmal ist es daher am besten, sich an Zeichenfolgen zu halten, die nur ASCII-Zeichen enthalten.
Wenn Sie jedoch die Ausgabe Ihres Programms umleiten oder weiterleiten , ist es im Allgemeinen nicht möglich, die Eingabecodierung des empfangenden Programms zu kennen, und der obige Code gibt eine Standardcodierung zurück: Keine (Python 2.7) oder UTF-8 ( Python 3):
% python2.7 -c "import sys; print sys.stdout.encoding" | cat
None
% python3.4 -c "import sys; print(sys.stdout.encoding)" | cat
UTF-8
Die Codierung von stdin, stdout und stderr kann jedoch werden gesetzt durch die PYTHONIOENCODING
Umgebungsvariable, falls erforderlich:
% PYTHONIOENCODING=UTF-8 python2.7 -c "import sys; print sys.stdout.encoding" | cat
UTF-8
Wenn der Druck auf ein Terminal nicht das liefert, was Sie erwarten, können Sie überprüfen, ob die manuell eingegebene UTF-8-Codierung korrekt ist. Zum Beispiel ist Ihr erstes Zeichen ( \u001A
) nicht druckbar, wenn ich mich nicht irre .
Unter http://wiki.python.org/moin/PrintFails finden Sie eine Lösung wie die folgende für Python 2.x:
import codecs
import locale
import sys
# Wrap sys.stdout into a StreamWriter to allow writing unicode.
sys.stdout = codecs.getwriter(locale.getpreferredencoding())(sys.stdout)
uni = u"\u001A\u0BC3\u1451\U0001D10C"
print uni
Für Python 3 können Sie eine der zuvor in StackOverflow gestellten Fragen überprüfen .