2015-08-02 10 views
8

ich beobachtet habe folgendes:Verketten Unicode mit string: print '£' + '1' funktioniert, aber print '£' + U'1' wirft UnicodeDecodeError

>>> print '£' + '1' 
£1 
>>> print '£' + u'1' 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc2 in position 0: ordinal not in range(128) 
>>> print u'£' + u'1' 
£1 
>>> print u'£' + '1' 
£1 

Warum '£' + '1' Arbeit aber '£' + u'1' funktioniert nicht?

schaute ich auf die Art:

>>> type('£' + '1') 
<type 'str'> 
>>> type('£' + u'1') 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc2 in position 0: ordinal not in range(128) 
>>> type(u'£' + u'1') 
<type 'unicode'> 

Dies auch mich verwirrt. Wenn '£' + '1' ein str und kein unicode ist, warum druckt es richtig auf meinem Terminal? Sollte es nicht so etwas wie '\xc2\xa31'?

drucken zu der Mischung hinzuzufügen, ich habe beobachtet, auch die folgende:

>>> u'£' + '1' 
u'\xa31' 
>>> type('1') 
<type 'str'> 
>>> type(u'£') 
<type 'unicode'> 
>>> print u'£' + '1' 
£1 

Warum u'£' + '1' nicht richtig aus dem £ Symbol drucken, während print u'£' + '1' tut? Ist es, weil repr in ersterem verwendet wird, während str in letzterem verwendet wird?

Auch wie funktioniert die Verkettung von unicode und str in diesem Fall, aber nicht in der '£' + u'1' Fall?

+0

Afaik Sie können nur Concat Strings von der gleicher Typ, dh 'u' £ '+ u'1' 'oder' £ '+' 1 ''. Du kannst sie nicht mischen. – Bjorn

+0

Sie versuchen, mit 'print '' + u'1'' als ASCII zu dekodieren, Sie werden' '\ xc2 \ xa31'' niemals beim Drucken sehen, es sei denn, Sie drucken das' repr' des Objekts, ' print '£' + '1'' funktioniert, weil deine Shell so konfiguriert ist, dass sie utf-8 akzeptiert –

+0

@Bjorn Du kannst, ich habe es schon oft gemacht, siehe die aktualisierte Frage – texasflood

Antwort

9

Sie mischen Objekttypen.

'£' ist ein Byte-String, der codierte Daten enthält. Dass diese Bytes zufällig ein Pfundzeichen in Ihrem Terminal oder Ihrer Konsole darstellen, ist weder hier noch dort, es könnte genauso gut ein Pixel in einem Bild gewesen sein. Ihr Terminal oder Ihre Konsole ist so konfiguriert, dass stattdessen UTF-8-Daten erzeugt und akzeptiert werden, so dass der tatsächliche Inhalt dieser Bytefolge die zwei Bytes C2 und A3 ist, wenn sie in Hexadezimalformat ausgegeben wird.

u'1' auf der anderen Seite ist ein Unicode-String. Es sind eindeutig Textdaten. Wenn Sie andere Daten verketten möchten, sollte auch Unicode verwendet werden. Python 2 decodiert dann automatisch str Bytes in Unicode mit dem Standard-ASCII-Codec, wenn Sie dies versuchen.

Der '£' Bytestring ist jedoch nicht als ASCII dekodierbar. Es kann als UTF-8 dekodiert werden; dekodieren explizit die Bytes, da wir den richtigen Codec hier wissen:

print '£'.decode('utf8') + u'1' 

Wenn Bytes an das Terminal oder Konsole zu schreiben, ist es Ihre Terminal oder Konsole, die die Bytes und macht Sinn, von ihnen interpretiert. Wenn Sie ein unicode-Objekt an das Terminal schreiben, kümmert sich das Objekt sys.stdout um die Codierung und konvertiert den Text in Bytes, die Ihr Terminal oder Ihre Konsole verstehen.

Das gleiche gilt für die Eingabe; Der Stream sys.stdin erzeugt Bytes, die Python transparent dekodieren kann, wenn Sie die Syntax u'£' verwenden, um ein Unicode-Objekt zu erstellen. Sie geben das Zeichen auf der Tastatur ein, es wird vom Terminal oder der Konsole in UTF-8-Bytes übersetzt und zur Interpretation in Python geschrieben.

Das Schreiben '\xc2\xa3' mit print funktioniert, dann ist ein glücklicher Zufall. Sie könnten nehmen Sie das unicode Objekt, kodieren sie zu einem verschiedenen Codec und mit Müll Ausgabe am Ende:

>>> print u'£1'.encode('latin-1') 
?1 

My Mac Terminal umgewandelt, die für die £ Zeichen geschriebenen Daten zu einem ?, weil der A3-Byte (Der Latin-1-Codepunkt für das Pfundzeichen wird bei der Interpretation als UTF-8 nicht zugeordnet.

Python bestimmt das Terminal oder Konsole Codec aus der locale.getpreferredencoding() function, können Sie beobachten, was Ihr Terminal oder Konsole kommuniziert es über die sys.stdout.encoding und sys.stdin.encoding Attribute verwendet:

>>> import sys 
>>> sys.stdout.encoding 
'UTF-8' 

Last but not least, sollten Sie nicht verwirren Drucken mit den Darstellungen, die vom Interpreter im interaktiven Modus wiedergegeben werden. Der Interpreter zeigt das Ergebnis von Ausdrücken unter Verwendung der repr()-Funktion an, einem Debugging-Tool, das versucht, wo immer möglich Python-Literalnotation zu erzeugen, wobei nur ASCII-Zeichen verwendet werden. Für Unicode-Werte bedeutet das, dass nicht druckbares Nicht-ASCII-Zeichen mit Escape-Sequenzen wiedergegeben wird. Dies macht den Wert zum Kopieren und Einfügen geeignet, ohne dass mehr als ein ASCII-fähiges Medium benötigt wird.

repr() Das Ergebnis eines str verwendet \n für Zeilenumbrüche, zum Beispiel, und \xhh hex entweicht für Bytes ohne dedizierte Escape-Sequenzen, außerhalb des Druckbereichs. Darüber hinaus ist für unicode Objekte, Codepunkte außerhalb des Latin-1-Bereich vertreten sind mit \uhhhh und \Uhhhhhhhh Escape-Sequenzen je nach Wetter oder nicht sie einen Teil der Basic Multilingual Plane sind:

>>> u'''\ 
... A multiline string to show newlines 
... can contain £ latin characters 
... or emoji ! 
... ''' 
u'A multiline string to show newlines\ncan contain \xa3 latin characters\nor emoji \U0001f4a9!\n' 
>>> print _ 
A multiline string to show newlines 
can contain £ latin characters 
or emoji ! 
+0

OK danke. Also funktioniert 'u' £ '+' 1'', weil die '' 1'' als UTF-8 dekodiert werden kann? – texasflood

+0

@texasflood: ''1'' kann als *** ASCII *** dekodiert werden. –

+0

Aber dann gibt 'u' £ '+' 1'' ein Unicode-Objekt zurück, also wie kombiniert es die ASCII- und UTF-8-Objekte? Ich hätte gedacht, dass es "1" in sein UTF-8-Äquivalent umwandelt und dann zwei UTF-8-Objekte verkettet, was trivial ist. – texasflood