2015-08-02 32 views
8

jakie obserwowano następujące:złączenie Unicode sznurkiem: print '£' + '1' działa, ale print '£' + u'1' rzuca 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 

Dlaczego '£' + '1' praca ale '£' + u'1' nie działa?

Patrzyłem na typy:

>>> 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'> 

to również myli mnie. Jeśli '£' + '1' jest str, a nie unicode, dlaczego jest poprawnie drukowany na moim terminalu? Nie należy drukować coś '\xc2\xa31'?

Aby dodać do mieszanki, ja również przestrzegać następujących czynności:

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

Dlaczego u'£' + '1' nie wydrukować £ symbolem prawidłowo, natomiast print u'£' + '1' robi? Czy to dlatego, że w pierwszym zastosowano repr, podczas gdy w tym ostatnim jest str?

Co więcej, jak wygląda połączenie w tym przypadku unicode i str, ale nie w przypadku '£' + u'1'?

+0

AFAIK można tylko concat struny ten sam typ, tj. 'u '£' + u'1'' lub' '£' + '1''. Nie możesz ich mieszać. – Bjorn

+0

Próbujesz rozszyfrować jako ascii za pomocą 'print '£' + u'1'', nigdy nie zobaczysz' '\ xc2 \ xa31'' kiedy drukujesz, chyba że wydrukujesz' repr' obiektu, ' print '£' + '1'' działa, ponieważ twoja powłoka jest skonfigurowana do akceptowania utf-8 –

+0

@Bjorn Możesz, zrobiłem to wiele razy, zobacz zaktualizowane pytanie – texasflood

Odpowiedz

9

Mieszane są typy obiektów .

'£' to bytestring, zawierający zakodowane dane. To, że te bajty przedstawiają znak funta w twoim terminalu lub konsoli, ani tu, ani tam, może być tak samo jak piksel na obrazie. Twój terminal lub konsola jest skonfigurowana tak, aby produkować i akceptować dane UTF-8, więc zawartość tego testu to dwa bajtów C2 i A3, po wyrażeniu w postaci szesnastkowej.

u'1' z drugiej strony jest ciągiem Unicode. Jest to jednoznaczne dane tekstowe. Jeśli chcesz dołączyć do niego inne dane, to również powinien to być kod Unicode. Python 2 następnie automatycznie zdekoduje str bajtów do Unicode za pomocą domyślnego kodeka ASCII, jeśli spróbujesz to zrobić.

Mimo to, testowanie '£' nie podlega dekodowaniu jako ASCII. To może być dekodowane jako UTF-8; zdekodować bajty jednoznacznie, ponieważ wiemy prawidłowego kodeka tutaj:

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

Pisząc bajtów do terminala lub konsoli, to terminal lub konsola który interpretuje bajty i sensowne z nich. Jeśli napiszesz obiekt unicode do terminala, obiekt sys.stdout zajmie się kodowaniem, konwertując tekst na bajty, które zrozumie twój terminal lub konsola.

To samo dotyczy przyjmowania danych wejściowych; strumień sys.stdin generuje bajty, które Python może dekodować przezroczysto podczas używania składni u'£' do utworzenia obiektu Unicode. Wpisujesz znak na klawiaturze, jest on tłumaczony na bajty UTF-8 przez terminal lub konsolę i zapisywany w Pythonie w celu zinterpretowania.

Pisanie '\xc2\xa3' z print działa, więc jest szczęśliwym zbiegiem okoliczności. Można wziąć przedmiot unicode, zakodować do różnych kodeka, a kończy się z wyjściem na śmieci:

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

zacisk My Mac przekształcone dane zapisane na znak £ do ?, ponieważ bajt A3 (numer kodowy Latin-1 dla znaku funta) nie mapuje się do niczego po interpretacji jako UTF-8.

Python określa kodek terminala lub konsoli z locale.getpreferredencoding() function, można obserwować co terminal lub konsola przekazane używa poprzez sys.stdout.encoding i sys.stdin.encoding atrybutów:

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

Last but not least, nie należy mylić drukowanie z reprezentacjami wyświetlanymi przez interpreter w trybie interaktywnym. Interpreter pokazuje wynik wyrażeń za pomocą funkcji repr(), narzędzia do debugowania, które próbuje utworzyć literacką notację w języku Python wszędzie tam, gdzie jest to możliwe, używając znaków ASCII w postaci tylko. W przypadku wartości Unicode oznacza to, że dowolny niedrukowalny, nie-ASCII znak jest odbijany przy użyciu sekwencji ucieczki. To sprawia, że ​​wartość nadaje się do kopiowania i wklejania bez konieczności używania więcej niż medium obsługującego ASCII.

repr() wynikiem str wykorzystuje \n do nowej linii, na przykład, sześciokątny \xhh uchodzi do bajtów bez określonych sekwencje, do strefy drukowania. Ponadto, dla unicode obiektów codepoints poza zakresu Latin-1 są reprezentowane z \uhhhh i \Uhhhhhhhh sekwencje w zależności od wether czy nie są one częścią podstawowego samolotu wielojęzycznym:

>>> 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 dziękuję. Więc 'u '£' + '1'' działa, ponieważ'' 1'' może zostać zdekodowany jako UTF-8? – texasflood

+0

@texasflood: '' 1'' może dekodować jako *** ASCII ***. –

+0

Ale wtedy 'u '£' + '1'' zwraca obiekt unicode, więc w jaki sposób łączy obiekty ASCII i UTF-8? Myślę, że zamieniłoby "1'' na jego odpowiednik w UTF-8, a następnie zsumowało dwa obiekty UTF-8, co jest banalne. – texasflood