Z powłoki Pythona 2.6:
>>> import sys
>>> print sys.getdefaultencoding()
ascii
>>> print u'\xe9'
é
>>>
Spodziewałem się jakiegoś bełkotu lub błędu po instrukcji print, ponieważ znak „é” nie jest częścią ASCII i nie określiłem kodowania. Wydaje mi się, że nie rozumiem, co oznacza ASCII jako domyślne kodowanie.
EDYTOWAĆ
Przeniosłem zmianę do sekcji Odpowiedzi i zaakceptowałem ją zgodnie z sugestią.
'\xe9'
w terminalu skonfigurowanym dla UTF-8 nie będzie drukowaneé
. Wyświetli znak zastępczy (zwykle znak zapytania), ponieważ\xe9
nie jest prawidłową sekwencją UTF-8 (brakuje dwóch bajtów, które powinny następować po tym wiodącym bajcie). Z pewnością nie będzie to interpretowane jako Latin-1.\xe9
do drukowaniaé
.Odpowiedzi:
Myślę, że dzięki fragmentom z różnych odpowiedzi możemy zszyć wyjaśnienie.
Próbując wydrukować ciąg znaków Unicode, u '\ xe9', Python niejawnie próbuje zakodować ten ciąg przy użyciu schematu kodowania obecnie przechowywanego w sys.stdout.encoding. Python faktycznie pobiera to ustawienie ze środowiska, z którego został zainicjowany. Jeśli nie może znaleźć odpowiedniego kodowania w środowisku, tylko wtedy powraca do swojego domyślnego ASCII.
Na przykład używam powłoki bash, której kodowanie jest domyślnie ustawione na UTF-8. Jeśli uruchomię z niego Pythona, odbierze i użyje tego ustawienia:
Wyjdźmy na chwilę z powłoki Pythona i ustawmy środowisko basha z jakimś fałszywym kodowaniem:
Następnie ponownie uruchom powłokę Pythona i sprawdź, czy rzeczywiście powraca do domyślnego kodowania ascii.
Bingo!
Jeśli teraz spróbujesz wypisać jakiś znak Unicode poza ascii, powinieneś otrzymać ładny komunikat o błędzie
Zamknijmy Pythona i odrzućmy powłokę bash.
Teraz będziemy obserwować, co się dzieje po tym, jak Python wyprowadza ciągi. W tym celu najpierw uruchomimy powłokę bash w terminalu graficznym (używam terminala Gnome) i ustawimy terminal na dekodowanie wyjścia za pomocą ISO-8859-1 aka latin-1 (terminale graficzne zwykle mają opcję Ustaw znak Kodowanie w jednym z rozwijanych menu). Zauważ, że nie zmienia to rzeczywistego kodowania środowiska powłoki , zmienia tylko sposób, w jaki terminal sam dekoduje dane wyjściowe, podobnie jak robi to przeglądarka internetowa. Możesz zatem zmienić kodowanie terminala, niezależnie od środowiska powłoki. Zacznijmy więc Pythona od powłoki i sprawdźmy, czy sys.stdout.encoding jest ustawione na kodowanie środowiska powłoki (dla mnie UTF-8):
(1) python wysyła ciąg binarny bez zmian, terminal odbiera go i próbuje dopasować jego wartość do mapy znaków latin-1. W Latin-1, 0xe9 lub 233 daje znak „é”, więc to właśnie wyświetla terminal.
(2) python próbuje niejawnie zakodować ciąg znaków Unicode za pomocą dowolnego schematu ustawionego w sys.stdout.encoding, w tym przypadku jest to „UTF-8”. Po zakodowaniu UTF-8 otrzymany ciąg binarny to „\ xc3 \ xa9” (zobacz późniejsze wyjaśnienie). Terminal odbiera strumień jako taki i próbuje zdekodować kod 0xc3a9 przy użyciu latin-1, ale latin-1 przechodzi od 0 do 255, a więc dekoduje tylko strumienie 1 bajt na raz. 0xc3a9 ma długość 2 bajtów, dekoder latin-1 interpretuje go zatem jako 0xc3 (195) i 0xa9 (169), co daje 2 znaki: Ă i ©.
(3) Python koduje punkt kodowy Unicode u '\ xe9' (233) ze schematem latin-1. Okazuje się, że zakres punktów kodowych Latin-1 to 0-255 i wskazuje dokładnie ten sam znak, co Unicode w tym zakresie. Dlatego punkty kodowe Unicode w tym zakresie będą dawały tę samą wartość, gdy zostaną zakodowane w latin-1. Więc u '\ xe9' (233) zakodowane w latin-1 również da ciąg binarny '\ xe9'. Terminal otrzymuje tę wartość i próbuje dopasować ją na mapie znaków latin-1. Podobnie jak przypadek (1), zwraca „é” i to jest wyświetlane.
Zmieńmy teraz ustawienia kodowania terminala na UTF-8 z menu rozwijanego (tak jakbyś zmienił ustawienia kodowania przeglądarki internetowej). Nie ma potrzeby zatrzymywania Pythona ani restartowania powłoki. Kodowanie terminala jest teraz zgodne z kodowaniem Pythona. Spróbujmy wydrukować ponownie:
(4) Python wyprowadza ciąg binarny bez zmian. Terminal próbuje zdekodować ten strumień za pomocą UTF-8. Ale UTF-8 nie rozumie wartości 0xe9 (patrz późniejsze wyjaśnienie) i dlatego nie jest w stanie przekonwertować jej na punkt kodowy Unicode. Nie znaleziono punktu kodowego, brak wydrukowanego znaku.
(5) Python próbuje niejawnie zakodować ciąg znaków Unicode za pomocą tego, co jest w sys.stdout.encoding. Nadal „UTF-8”. Wynikowy ciąg binarny to „\ xc3 \ xa9”. Terminal odbiera strumień i próbuje zdekodować 0xc3a9 również przy użyciu UTF-8. Zwraca wartość kodu 0xe9 (233), która na mapie znaków Unicode wskazuje na symbol „é”. Terminal wyświetla „é”.
(6) Python koduje ciąg znaków Unicode za pomocą latin-1, daje ciąg binarny o tej samej wartości '\ xe9'. Ponownie, w przypadku terminala jest to prawie to samo, co w przypadku (4).
Wnioski: - Python wyprowadza ciągi nie-Unicode jako surowe dane, bez uwzględnienia domyślnego kodowania. Terminal po prostu wyświetla je, jeśli jego bieżące kodowanie jest zgodne z danymi. - Python wyprowadza ciągi Unicode po zakodowaniu ich przy użyciu schematu określonego w sys.stdout.encoding. - Python pobiera to ustawienie ze środowiska powłoki. - terminal wyświetla dane wyjściowe zgodnie z własnymi ustawieniami kodowania. - kodowanie terminala jest niezależne od powłoki.
Więcej szczegółów na temat Unicode, UTF-8 i latin-1:
Unicode to w zasadzie tablica znaków, w której niektóre klawisze (punkty kodowe) zostały tradycyjnie przypisane do wskazania niektórych symboli. np. konwencją zdecydowano, że klucz 0xe9 (233) jest wartością wskazującą na symbol „é”. ASCII i Unicode używają tych samych punktów kodowych od 0 do 127, podobnie jak latin-1 i Unicode od 0 do 255. Oznacza to, że 0x41 wskazuje „A” w ASCII, latin-1 i Unicode, 0xc8 wskazuje „Ü” w latin-1 i Unicode, 0xe9 wskazuje na „é” w latin-1 i Unicode.
Podczas pracy z urządzeniami elektronicznymi punkty kodowe Unicode wymagają wydajnego sposobu reprezentacji elektronicznej. O to chodzi w schematach kodowania. Istnieją różne schematy kodowania Unicode (utf7, UTF-8, UTF-16, UTF-32). Najbardziej intuicyjnym i prostym podejściem do kodowania byłoby po prostu użycie wartości punktu kodowego w mapie Unicode jako wartości jej formy elektronicznej, ale Unicode ma obecnie ponad milion punktów kodowych, co oznacza, że niektóre z nich wymagają 3 bajtów wyrażone. Aby efektywnie pracować z tekstem, mapowanie 1 do 1 byłoby raczej niepraktyczne, ponieważ wymagałoby, aby wszystkie punkty kodowe były przechowywane w dokładnie tej samej ilości miejsca, z minimum 3 bajtami na znak, niezależnie od ich rzeczywistych potrzeb.
Większość schematów kodowania ma wady związane z wymaganiami dotyczącymi miejsca, najbardziej ekonomiczne nie obejmują wszystkich punktów kodowych Unicode, na przykład ascii obejmuje tylko pierwsze 128, podczas gdy latin-1 obejmuje pierwsze 256. Inne, które starają się być bardziej wszechstronne, również kończą marnotrawstwo, ponieważ wymagają więcej bajtów niż to konieczne, nawet w przypadku typowych „tanich” znaków. Na przykład UTF-16 wykorzystuje co najmniej 2 bajty na znak, w tym te z zakresu ascii („B”, które wynosi 65, nadal wymaga 2 bajtów pamięci w UTF-16). UTF-32 jest jeszcze bardziej marnotrawny, ponieważ przechowuje wszystkie znaki w 4 bajtach.
UTF-8 sprytnie rozwiązał ten dylemat za pomocą schematu zdolnego do przechowywania punktów kodowych ze zmienną ilością przestrzeni bajtowych. W ramach swojej strategii kodowania UTF-8 łączy punkty kodowe z bitami flag, które wskazują (prawdopodobnie dekoderom) ich wymagania dotyczące przestrzeni i ich granice.
Kodowanie UTF-8 punktów kodowych Unicode w zakresie ascii (0-127):
np. punkt kodowy Unicode dla „B” to „0x42” lub 0100 0010 w postaci binarnej (jak powiedzieliśmy, jest to to samo w ASCII). Po zakodowaniu w UTF-8 staje się:
Kodowanie UTF-8 punktów kodowych Unicode powyżej 127 (nie ASCII):
np. „é” Punkt kodowy Unicode to 0xe9 (233).
Kiedy UTF-8 koduje tę wartość, określa, że wartość jest większa niż 127 i mniejsza niż 2048, dlatego powinna być zakodowana w 2 bajtach:
Punkty kodowe 0xe9 Unicode po kodowaniu UTF-8 stają się 0xc3a9. Dokładnie tak, jak odbiera go terminal. Jeśli twój terminal jest ustawiony na dekodowanie łańcuchów przy użyciu latin-1 (jednego ze starszych kodowań innych niż Unicode), zobaczysz à ©, ponieważ tak się składa, że 0xc3 w Latin-1 wskazuje na à i 0xa9 na ©.
źródło
Gdy znaki Unicode są drukowane na standardowe wyjście,
sys.stdout.encoding
jest używane. Zakłada się, że znak inny niż Unicode znajduje się w środkusys.stdout.encoding
i jest po prostu wysyłany do terminala. W moim systemie (Python 2):sys.getdefaultencoding()
jest używany tylko wtedy, gdy Python nie ma innej opcji.Zauważ, że Python 3.6 lub nowszy ignoruje kodowanie w systemie Windows i używa interfejsów API Unicode do zapisywania Unicode na terminalu. Brak ostrzeżeń UnicodeEncodeError i wyświetlany jest prawidłowy znak, jeśli czcionka go obsługuje. Nawet jeśli czcionka go nie obsługuje, znaki nadal można wycinać i wklejać z terminala do aplikacji z czcionką pomocniczą i będzie to poprawne. Aktualizacja!
źródło
Python REPL próbuje pobrać kodowanie z twojego środowiska. Jeśli znajdzie coś rozsądnego, to wszystko po prostu działa. To wtedy, gdy nie może dowiedzieć się, co się dzieje, robi to źle.
źródło
TypeError: readonly attribute
na 2.7.2Państwo nie określono kodowania, wprowadzając wyraźne ciąg Unicode. Porównaj wyniki nieużywania
u
przedrostka.W takim przypadku
\xe9
Python zakłada domyślne kodowanie (Ascii), wypisując w ten sposób ... coś pustego.źródło
Mi to pasuje:
źródło
Zgodnie z domyślnym / niejawnym kodowaniem ciągów i konwersjami w języku Python :
print
ingunicode
, toencode
d z<file>.encoding
.encoding
nie jest ustawiona, tounicode
jest niejawnie konwertowana nastr
(ponieważ kodek do tego jestsys.getdefaultencoding()
, tj.ascii
dowolny znak narodowy spowodowałby aUnicodeEncodeError
)encoding
jest on wywnioskowany ze środowiska. Zwykle jest to ustawione dlatty
strumieni (z ustawień regionalnych terminala), ale prawdopodobnie nie zostanie ustawione dla potokówprint u'\xe9'
prawdopodobnie powiedzie się, gdy dane wyjściowe trafią do terminala, i zakończy się niepowodzeniem, jeśli zostanie przekierowany. Rozwiązaniem jestencode()
łańcuch z żądanym kodowaniem przed rozpoczęciemprint
.print
ingstr
bajty są wysyłane do strumienia bez zmian. To, jakie glify wyświetla terminal, zależy od jego ustawień regionalnych.źródło