Python - kodek „ascii” nie może dekodować bajtu

119

Jestem bardzo zmieszany. Próbowałem zakodować, ale błąd powiedział can't decode....

>>> "你好".encode("utf8")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe4 in position 0: ordinal not in range(128)

Wiem, jak uniknąć błędu z prefiksem „u” w ciągu. Zastanawiam się tylko, dlaczego błąd brzmi „nie można dekodować”, gdy wywołano kodowanie. Co robi Python pod maską?

thoslin
źródło

Odpowiedzi:

167
"你好".encode('utf-8')

encodekonwertuje obiekt Unicode na stringobiekt. Ale tutaj wywołałeś go na stringobiekcie (ponieważ nie masz u). Więc python musi najpierw przekonwertować obiekt stringna unicodeobiekt. Więc robi odpowiednik

"你好".decode().encode('utf-8')

Ale dekodowanie kończy się niepowodzeniem, ponieważ ciąg nie jest prawidłowym ascii. Dlatego otrzymujesz skargę dotyczącą braku możliwości dekodowania.

Winston Ewert
źródło
50
Więc jakie jest rozwiązanie? Zwłaszcza jeśli nie mam literału ciągu, po prostu mam obiekt typu string.
Jon Tirsen,
2
@JonTirsen, nie powinieneś kodować obiektu typu string. Obiekt typu string jest już zakodowany. Jeśli chcesz zmienić kodowanie, musisz zdekodować go na ciąg znaków Unicode, a następnie zakodować jako żądane kodowanie.
Winston Ewert,
20
Aby więc jasno to stwierdzić z góry, można"你好".decode('utf-8').encode('utf-8')
deinonychusaur
5
@WinstonEwert Chyba byłem zdezorientowany. Branża kodowania zwykle sprawia, że ​​jestem wiecznie zdezorientowany. Wydaje mi się, że moje zamieszanie wynikało z mojego własnego problemu, polegającego na tym, że nie wiedziałem, czy dane wejściowe są ciągiem znaków, czy unikodowym, i jakie może mieć kodowanie.
deinonychusaur
@deinonychusaur, tak ... Rozumiem.
Winston Ewert
53

Zawsze koduj od Unicode do bajtów.
W tym kierunku możesz wybrać kodowanie .

>>> u"你好".encode("utf8")
'\xe4\xbd\xa0\xe5\xa5\xbd'
>>> print _
你好

Innym sposobem jest dekodowanie z bajtów do Unicode.
W tym kierunku musisz wiedzieć, jakie jest kodowanie .

>>> bytes = '\xe4\xbd\xa0\xe5\xa5\xbd'
>>> print bytes
你好
>>> bytes.decode('utf-8')
u'\u4f60\u597d'
>>> print _
你好

Nie można tego wystarczająco podkreślić. Jeśli chcesz uniknąć grania w Unicode „whack-a-mole”, ważne jest, aby zrozumieć, co dzieje się na poziomie danych. Tutaj jest wyjaśnione w inny sposób:

  • Obiekt Unicode został już zdekodowany, nigdy nie chcesz go wywoływać decode.
  • Obiekt bytestring jest już zakodowany, nigdy nie chcesz go wywoływać encode.

Teraz, widząc .encodeciąg bajtów, Python 2 najpierw próbuje niejawnie przekonwertować go na tekst ( unicodeobiekt). Podobnie, widząc .decodeciąg znaków Unicode, Python 2 niejawnie próbuje przekonwertować go na bajty ( strobiekt).

Te niejawne konwersje są powodem, dla którego możesz uzyskać, gdy zadzwonisz . Dzieje się tak, ponieważ kodowanie zwykle akceptuje parametr typu ; podczas odbierania parametru następuje niejawne dekodowanie do obiektu typu przed ponownym zakodowaniem go za pomocą innego kodowania. Ta konwersja wybiera domyślny dekoder „ascii” , podając błąd dekodowania wewnątrz kodera.UnicodeDecodeErrorencodeunicodestrunicode

W rzeczywistości w Pythonie 3 metody str.decodei bytes.encodenawet nie istnieją. Ich usunięcie było [kontrowersyjną] próbą uniknięcia tego powszechnego zamieszania.

... lub cokolwiek sys.getdefaultencoding()wspomina kod; zwykle jest to „ascii”

wim
źródło
Więc masz na myśli, że Python dekoduje bajt przed kodowaniem?
thoslin
@thoslin dokładnie, dodałem więcej szczegółów.
wim
Co to jest _ i dlaczego w Twoich instrukcjach drukowanych brakuje nawiasów?
NoBugs
1
@NoBugs 1. w REPL _odnosi się do poprzedniej wartości 2. ponieważ jest to pytanie Python-2.x.
wim
40

Możesz tego spróbować

import sys
reload(sys)
sys.setdefaultencoding("utf-8")

Lub

Możesz także spróbować śledzić

Dodaj następujący wiersz u góry pliku .py.

# -*- coding: utf-8 -*- 
Dadaso Zanzane
źródło
8

Jeśli używasz Pythona <3, musisz powiedzieć interpreterowi, że twój literał ciągu to Unicode, poprzedzając gou :

Python 2.7.2 (default, Jan 14 2012, 23:14:09) 
[GCC 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2335.15.00)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> "你好".encode("utf8")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe4 in position 0: ordinal not in range(128)
>>> u"你好".encode("utf8")
'\xe4\xbd\xa0\xe5\xa5\xbd'

Dalsza lektura : Unicode HOWTO .

Johnsyweb
źródło
4
Jeśli kodujesz ciąg, dlaczego generuje on błąd dekodowania?
MxLDevs
3

Używasz u"你好".encode('utf8')do kodowania ciągu znaków Unicode. Ale jeśli chcesz to przedstawić "你好", powinieneś to zdekodować. Tak jak:

"你好".decode("utf8")

Dostaniesz to, czego chcesz. Może powinieneś dowiedzieć się więcej o kodowaniu i dekodowaniu.

Qingtian
źródło
3

Jeśli masz do czynienia z Unicode, czasami zamiast tego encode('utf-8')możesz spróbować zignorować znaki specjalne, np

"你好".encode('ascii','ignore')

lub jak something.decode('unicode_escape').encode('ascii','ignore')sugerowano tutaj .

Niezbyt przydatne w tym przykładzie, ale może działać lepiej w innych scenariuszach, gdy nie można przekonwertować niektórych znaków specjalnych.

Alternatywnie możesz rozważyć zastąpienie określonego znaku za pomocąreplace() .

kenorb
źródło
1

Jeśli uruchamiasz interpreter Pythona z powłoki na Linuksie lub podobnych systemach (BSD, nie jestem pewien co do Maca), powinieneś również sprawdzić domyślne kodowanie powłoki.

Zadzwoń locale charmapz powłoki (nie interpretera Pythona) i powinieneś zobaczyć

[user@host dir] $ locale charmap
UTF-8
[user@host dir] $ 

Jeśli tak nie jest, a widzisz coś innego, np

[user@host dir] $ locale charmap
ANSI_X3.4-1968
[user@host dir] $ 

Python (przynajmniej w niektórych przypadkach, takich jak mój) odziedziczy kodowanie powłoki i nie będzie w stanie wydrukować (niektórych? Wszystkich?) Znaków Unicode. Własne domyślne kodowanie Pythona, które można zobaczyć i sterować za pomocą, sys.getdefaultencoding()i sys.setdefaultencoding()jest w tym przypadku ignorowane.

Jeśli okaże się, że masz ten problem, możesz go naprawić, korzystając z pliku

[user@host dir] $ export LC_CTYPE="en_EN.UTF-8"
[user@host dir] $ locale charmap
UTF-8
[user@host dir] $ 

(Lub alternatywnie wybierz dowolną mapę klawiszy zamiast en_EN.) Możesz także edytować /etc/locale.conf(lub dowolny plik zarządzający definicją ustawień regionalnych w twoim systemie), aby to poprawić.

0range
źródło