Python - codec 'ascii' không thể giải mã byte


119

Tôi thực sự bối rối. Tôi đã cố gắng mã hóa nhưng lỗi cho biết 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)

Tôi biết cách tránh lỗi với tiền tố "u" trên chuỗi. Tôi chỉ tự hỏi tại sao lỗi "không thể giải mã" khi mã hóa được gọi. Python đang làm gì?

Câu trả lời:


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

encodechuyển đổi một đối tượng unicode thành một stringđối tượng. Nhưng ở đây bạn đã gọi nó trên một stringđối tượng (vì bạn không có u). Vì vậy, python phải chuyển đổi stringthành một unicodeđối tượng trước. Vì vậy, nó tương đương với

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

Nhưng giải mã không thành công vì chuỗi không hợp lệ ascii. Đó là lý do tại sao bạn nhận được khiếu nại về việc không thể giải mã.


50
vậy giải pháp là gì? Đặc biệt nếu tôi không có một chuỗi ký tự, tôi chỉ có một đối tượng chuỗi.
Jon Tirsen

2
@JonTirsen, bạn không nên mã hóa một đối tượng chuỗi. Một đối tượng chuỗi đã được mã hóa. Nếu bạn cần thay đổi bảng mã, bạn cần giải mã nó thành một chuỗi unicode và sau đó mã hóa nó thành bảng mã mong muốn.
Winston Ewert

20
Vì vậy, để nêu rõ ràng từ trên cao bạn có thể"你好".decode('utf-8').encode('utf-8')
deinonychusaur

5
@WinstonEwert Tôi đoán là tôi đã nhầm lẫn. Việc kinh doanh mã hóa có xu hướng khiến tôi vĩnh viễn bối rối. Tôi đoán sự nhầm lẫn của tôi đến từ vấn đề của riêng tôi khi không biết đầu vào là chuỗi hay chuỗi unicode và mã hóa nó có thể có.
deinonychusaur,

@deinonychusaur, vâng ... tôi hiểu rồi.
Winston Ewert,

53

Luôn mã hóa từ unicode sang byte.
Theo hướng này, bạn có thể chọn mã hóa .

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

Cách khác là giải mã từ byte sang unicode.
Theo hướng này, bạn phải biết mã hóa là gì .

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

Điểm này không thể đủ căng thẳng. Nếu bạn muốn tránh chơi unicode "whack-a-nốt ruồi", điều quan trọng là phải hiểu những gì đang xảy ra ở cấp dữ liệu. Ở đây nó được giải thích theo một cách khác:

  • Một đối tượng unicode đã được giải mã rồi, bạn không bao giờ muốn gọi decodenó.
  • Một đối tượng bytestring đã được mã hóa sẵn, bạn không bao giờ muốn gọi encodenó.

Bây giờ, khi nhìn thấy .encodemột chuỗi byte, Python 2 trước tiên cố gắng chuyển đổi nó thành văn bản (một unicodeđối tượng) một cách hoàn toàn . Tương tự, khi nhìn thấy .decodetrên một chuỗi unicode, Python 2 cố gắng chuyển đổi nó thành byte (một strđối tượng) một cách ngầm định .

Những chuyển đổi ngầm này là lý do tại sao bạn có thể nhận được khi bạn đã gọi . Đó là bởi vì mã hóa thường chấp nhận một tham số kiểu ; khi nhận một tham số, sẽ có một giải mã ngầm thành một đối tượng kiểu trước khi mã hóa lại nó bằng một mã hóa khác. Chuyển đổi này chọn một bộ giải mã 'ascii' mặc định , cho bạn lỗi giải mã bên trong bộ mã hóa.UnicodeDecodeErrorencodeunicodestrunicode

Trên thực tế, trong Python 3, các phương thức str.decodebytes.encodethậm chí không tồn tại. Việc loại bỏ họ là một nỗ lực [gây tranh cãi] để tránh sự nhầm lẫn phổ biến này.

... hoặc bất kỳ sys.getdefaultencoding()đề cập mã hóa nào ; thường thì đây là 'ascii'


Vì vậy, bạn có nghĩa là Python giải mã bytestring trước khi mã hóa?
thoslin

@thoslin chính xác, tôi đã bổ sung thêm chi tiết.
wim

_ Là gì, và tại sao câu lệnh in của bạn thiếu dấu ngoặc đơn?
NoBugs

1
@NoBugs 1. trong REPL, _đề cập đến giá trị trước đó 2. vì đây là câu hỏi python-2.x.
wim

40

Bạn có thể thử cái này

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

Hoặc là

Bạn cũng có thể thử làm theo

Thêm dòng sau vào đầu tệp .py của bạn.

# -*- coding: utf-8 -*- 

8

Nếu bạn đang sử dụng Python <3, bạn sẽ cần thông báo với trình thông dịch rằng chuỗi ký tựu của bạn là Unicode bằng cách đặt tiền tố cho nó bằng :

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'

Đọc thêm : Unicode HOWTO .


4
Nếu bạn đang mã hóa một chuỗi, tại sao nó lại gặp lỗi giải mã?
MxLDevs

3

Bạn sử dụng u"你好".encode('utf8')để mã hóa một chuỗi unicode. Nhưng nếu bạn muốn đại diện "你好", bạn nên giải mã nó. Giống như:

"你好".decode("utf8")

Bạn sẽ có được những gì bạn muốn. Có lẽ bạn nên tìm hiểu thêm về mã hóa và giải mã.


3

Trong trường hợp bạn đang xử lý Unicode, đôi khi thay vì encode('utf-8'), bạn cũng có thể thử bỏ qua các ký tự đặc biệt, ví dụ:

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

hoặc như được something.decode('unicode_escape').encode('ascii','ignore')đề xuất ở đây .

Không đặc biệt hữu ích trong ví dụ này, nhưng có thể hoạt động tốt hơn trong các trường hợp khác khi không thể chuyển đổi một số ký tự đặc biệt.

Ngoài ra, bạn có thể xem xét thay thế ký tự cụ thể bằng cách sử dụngreplace() .


1

Nếu bạn đang khởi động trình thông dịch python từ shell trên Linux hoặc các hệ thống tương tự (BSD, không chắc chắn về Mac), bạn cũng nên kiểm tra mã hóa mặc định cho shell.

Gọi locale charmaptừ trình bao (không phải trình thông dịch python) và bạn sẽ thấy

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

Nếu đây không phải là trường hợp, và bạn thấy một cái gì đó khác, ví dụ:

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

Python sẽ (ít nhất là trong một số trường hợp, chẳng hạn như trong trường hợp của tôi) kế thừa mã hóa của shell và sẽ không thể in (một số? Tất cả?) Các ký tự unicode. Mã hóa mặc định của riêng Python mà bạn thấy và kiểm soát thông qua sys.getdefaultencoding()sys.setdefaultencoding()trong trường hợp này bị bỏ qua.

Nếu bạn thấy mình gặp sự cố này, bạn có thể khắc phục bằng cách

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

(Hoặc chọn bất kỳ sơ đồ bàn phím nào bạn muốn thay vì en_EN.) Bạn cũng có thể chỉnh sửa /etc/locale.conf(hoặc bất kỳ tệp nào chi phối định nghĩa ngôn ngữ trong hệ thống của bạn) để sửa điều này.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.