Python - codec 'ascii' tidak dapat mendekode byte

119

Saya sangat bingung. Saya mencoba menyandikan tetapi kesalahannya mengatakan 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)

Saya tahu bagaimana menghindari kesalahan dengan awalan "u" pada string. Saya hanya ingin tahu mengapa kesalahannya adalah "tidak dapat memecahkan kode" saat penyandian dipanggil. Apa yang dilakukan Python di bawah tenda?

thoslin.dll
sumber

Jawaban:

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

encodemengonversi objek unicode menjadi stringobjek. Tapi di sini Anda telah memanggilnya pada sebuah stringobjek (karena Anda tidak memiliki u). Jadi python harus mengonversi stringmenjadi unicodeobjek terlebih dahulu. Jadi itu setara dengan

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

Tetapi decode gagal karena string tersebut tidak valid ascii. Itulah mengapa Anda mendapat keluhan tentang tidak dapat memecahkan kode.

Winston Ewert
sumber
50
Jadi apa solusinya? Apalagi jika saya tidak memiliki literal string, saya hanya memiliki objek string.
Jon Tirsen
2
@JonTirsen, Anda tidak boleh menyandikan objek string. Objek string sudah dienkode. Jika Anda perlu mengubah pengkodean, Anda perlu mendekodekannya menjadi string unicode dan kemudian mengenkodenya sebagai pengkodean yang diinginkan.
Winston Ewert
20
Jadi untuk menyatakannya dengan jelas dari atas, Anda dapat"你好".decode('utf-8').encode('utf-8')
deinonychusaur
5
@WinstonEwert Saya kira saya bingung. Bisnis encoding cenderung membuat saya bingung selamanya. Saya kira kebingungan saya berasal dari masalah saya sendiri karena tidak mengetahui apakah inputnya adalah string atau string unicode dan pengkodean apa yang mungkin dimilikinya.
deinonychusaur
@deinonychusaur, ya ... Saya mengerti.
Winston Ewert
53

Selalu encode dari unicode ke byte.
Dalam arah ini, Anda dapat memilih pengkodean .

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

Cara lainnya adalah dengan memecahkan kode dari byte ke unicode.
Dalam arah ini, Anda harus tahu apa itu pengkodean .

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

Poin ini tidak bisa cukup ditekankan. Jika Anda ingin menghindari bermain unicode "whack-a-mole", penting untuk memahami apa yang terjadi di tingkat data. Di sini dijelaskan dengan cara lain:

  • Objek unicode sudah diterjemahkan, Anda tidak ingin memanggilnya decode.
  • Objek bytestring sudah dikodekan, Anda tidak akan pernah ingin memanggilnya encode.

Sekarang, saat melihat .encodepada string byte, Python 2 pertama kali mencoba mengubahnya secara implisit menjadi teks ( unicodeobjek). Demikian pula, saat melihat .decodepada string unicode, Python 2 secara implisit mencoba mengubahnya menjadi byte ( strobjek).

Konversi implisit ini adalah mengapa Anda bisa mendapatkan saat Anda menelepon . Itu karena encoding biasanya menerima parameter tipe ; saat menerima parameter, ada dekode implisit ke dalam objek berjenis sebelum melakukan encoding ulang dengan encoding lain. Konversi ini memilih decoder 'ascii' default , memberikan Anda kesalahan decoding di dalam encoder.UnicodeDecodeErrorencodeunicodestrunicode

Faktanya, dalam Python 3 metode str.decodedanbytes.encode bahkan tidak ada. Penghapusan mereka adalah upaya [kontroversial] untuk menghindari kebingungan umum ini.

... atau kode apa pun yang sys.getdefaultencoding()disebutkan; biasanya ini adalah 'ascii'

wim
sumber
Jadi maksud Anda bahwa Python mendekode bytestring sebelum encoding?
thoslin
@thoslin tepatnya, saya menambahkan lebih banyak detail.
wim
Apa itu _, dan mengapa pernyataan cetak Anda tidak memiliki tanda kurung?
NoBugs
1
@NoBugs 1. di REPL, _mengacu pada nilai sebelumnya 2. karena ini adalah pertanyaan python-2.x.
wim
40

Anda bisa mencobanya

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

Atau

Anda juga dapat mencoba mengikuti

Tambahkan baris berikut di atas file .py Anda.

# -*- coding: utf-8 -*- 
Dadaso Zanzane
sumber
8

Jika Anda menggunakan Python <3, Anda harus memberi tahu penerjemah bahwa literal stringu Anda adalah Unicode dengan mengawali dengan :

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'

Bacaan lebih lanjut : Unicode HOWTO .

Johnsyweb
sumber
4
Jika Anda mengenkode string, mengapa itu memunculkan kesalahan decode?
MxLDevs
3

Anda gunakan u"你好".encode('utf8')untuk menyandikan string unicode. Tetapi jika Anda ingin mewakili "你好", Anda harus mendekodekannya. Seperti:

"你好".decode("utf8")

Anda akan mendapatkan apa yang Anda inginkan. Mungkin Anda harus mempelajari lebih lanjut tentang encode & decode.

Qingtian
sumber
3

Jika Anda berurusan dengan Unicode, terkadang alih-alih encode('utf-8'), Anda juga dapat mencoba mengabaikan karakter khusus, mis

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

atau seperti yang something.decode('unicode_escape').encode('ascii','ignore')disarankan di sini .

Tidak terlalu berguna dalam contoh ini, tetapi dapat bekerja lebih baik dalam skenario lain ketika beberapa karakter khusus tidak memungkinkan.

Atau Anda dapat mempertimbangkan untuk mengganti karakter tertentu menggunakanreplace() .

kenorb
sumber
1

Jika Anda memulai penerjemah python dari shell di Linux atau sistem serupa (BSD, tidak yakin tentang Mac), Anda juga harus memeriksa pengkodean default untuk shell.

Panggil locale charmapdari shell (bukan interpreter python) dan Anda akan melihatnya

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

Jika tidak demikian, dan Anda melihat sesuatu yang lain, mis

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

Python akan (setidaknya dalam beberapa kasus seperti dalam kasus saya) mewarisi pengkodean shell dan tidak akan dapat mencetak (beberapa? Semua?) Karakter unicode. Pengkodean default Python yang Anda lihat dan kontrol melalui sys.getdefaultencoding()dansys.setdefaultencoding() dalam hal ini diabaikan.

Jika ternyata Anda mengalami masalah ini, Anda dapat memperbaikinya dengan

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

(Atau sebagai alternatif, pilih peta kunci mana saja yang Anda inginkan daripada en_EN.) Anda juga dapat mengedit /etc/locale.conf(atau file mana pun yang mengatur definisi lokal di sistem Anda) untuk memperbaikinya.

0range
sumber