tl; dr / sửa chữa nhanh
Unicode Zen trong Python 2.x - Phiên bản dài
Không nhìn thấy nguồn, thật khó để biết nguyên nhân gốc rễ, vì vậy tôi sẽ phải nói chung.
UnicodeDecodeError: 'ascii' codec can't decode byte
thường xảy ra khi bạn cố gắng chuyển đổi Python 2.x str
có chứa không phải ASCII thành chuỗi Unicode mà không chỉ định mã hóa của chuỗi gốc.
Tóm lại, chuỗi Unicode là một loại chuỗi Python hoàn toàn riêng biệt không chứa bất kỳ mã hóa nào. Họ chỉ giữ mã điểm Unicode và do đó có thể giữ bất kỳ điểm Unicode nào trên toàn bộ phổ. Các chuỗi chứa văn bản được mã hóa, beit UTF-8, UTF-16, ISO-8895-1, GBK, Big5, v.v. Các chuỗi được giải mã thành Unicode và Unicodes được mã hóa thành chuỗi . Các tệp và dữ liệu văn bản luôn được chuyển trong các chuỗi được mã hóa.
Các tác giả mô-đun Markdown có thể sử dụng unicode()
(nơi ném ngoại lệ) làm cổng chất lượng cho phần còn lại của mã - nó sẽ chuyển đổi ASCII hoặc bọc lại các chuỗi Unicodes hiện có thành chuỗi Unicode mới. Các tác giả Markdown không thể biết mã hóa chuỗi đến, vì vậy sẽ dựa vào bạn để giải mã chuỗi thành chuỗi Unicode trước khi chuyển sang Markdown.
Chuỗi Unicode có thể được khai báo trong mã của bạn bằng cách sử dụng u
tiền tố cho chuỗi. Ví dụ
>>> my_u = u'my ünicôdé strįng'
>>> type(my_u)
<type 'unicode'>
Chuỗi Unicode cũng có thể đến từ tệp, cơ sở dữ liệu và mô-đun mạng. Khi điều này xảy ra, bạn không cần phải lo lắng về việc mã hóa.
Gotchas
Chuyển đổi từ str
Unicode có thể xảy ra ngay cả khi bạn không gọi rõ ràng unicode()
.
Các tình huống sau đây gây ra UnicodeDecodeError
ngoại lệ:
# Explicit conversion without encoding
unicode('€')
# New style format string into Unicode string
# Python will try to convert value string to Unicode first
u"The currency is: {}".format('€')
# Old style format string into Unicode string
# Python will try to convert value string to Unicode first
u'The currency is: %s' % '€'
# Append string to Unicode
# Python will try to convert string to Unicode first
u'The currency is: ' + '€'
Ví dụ
Trong sơ đồ sau, bạn có thể thấy từ café
đã được mã hóa theo mã hóa "UTF-8" hoặc "Cp1252" tùy thuộc vào loại thiết bị đầu cuối. Trong cả hai ví dụ, caf
chỉ là ascii thông thường. Trong UTF-8, é
được mã hóa bằng hai byte. Trong "Cp1252", é là 0xE9 (cũng là giá trị điểm Unicode (không phải là ngẫu nhiên)). Chính xác decode()
được gọi và chuyển đổi sang Unicode Unicode là thành công:
Trong sơ đồ này, decode()
được gọi với ascii
(giống như gọi unicode()
mà không có mã hóa). Vì ASCII không thể chứa byte lớn hơn 0x7F
, điều này sẽ tạo ra một UnicodeDecodeError
ngoại lệ:
Sandwich Sandwich
Cách tốt nhất là tạo một bánh sandwich Unicode trong mã của bạn, nơi bạn giải mã tất cả dữ liệu đến thành chuỗi Unicode, làm việc với Unicodes, sau đó mã hóa thành str
s trên đường ra. Điều này giúp bạn không phải lo lắng về việc mã hóa chuỗi ở giữa mã của bạn.
Nhập / Giải mã
Mã nguồn
Nếu bạn cần nướng non-ASCII vào mã nguồn của mình, chỉ cần tạo các chuỗi Unicode bằng cách thêm tiền tố vào chuỗi u
. Ví dụ
u'Zürich'
Để cho phép Python giải mã mã nguồn của bạn, bạn sẽ cần thêm một tiêu đề mã hóa để khớp với mã hóa thực tế của tệp của bạn. Ví dụ: nếu tệp của bạn được mã hóa dưới dạng 'UTF-8', bạn sẽ sử dụng:
# encoding: utf-8
Điều này chỉ cần thiết khi bạn không có ASCII trong mã nguồn của mình .
Các tập tin
Thông thường dữ liệu không phải ASCII được nhận từ một tệp. Các io
mô-đun cung cấp một TextWrapper rằng giải mã tập tin của bạn một cách nhanh chóng, sử dụng một định encoding
. Bạn phải sử dụng mã hóa chính xác cho tệp - không thể dễ dàng đoán được. Ví dụ: đối với tệp UTF-8:
import io
with io.open("my_utf8_file.txt", "r", encoding="utf-8") as my_file:
my_unicode_string = my_file.read()
my_unicode_string
sau đó sẽ thích hợp để chuyển đến Markdown. Nếu a UnicodeDecodeError
từ read()
dòng, thì có lẽ bạn đã sử dụng giá trị mã hóa sai.
Tệp CSV
Mô-đun CSV 2.7 2.7 không hỗ trợ các ký tự không phải ASCII. Tuy nhiên, sự giúp đỡ đã sẵn sàng với https://pypi.python.org/pypi/backports.csv .
Sử dụng nó như trên nhưng chuyển tập tin đã mở cho nó:
from backports import csv
import io
with io.open("my_utf8_file.txt", "r", encoding="utf-8") as my_file:
for row in csv.reader(my_file):
yield row
Cơ sở dữ liệu
Hầu hết các trình điều khiển cơ sở dữ liệu Python có thể trả về dữ liệu bằng Unicode, nhưng thường yêu cầu một chút cấu hình. Luôn sử dụng chuỗi Unicode cho các truy vấn SQL.
MySQL
Trong chuỗi kết nối thêm:
charset='utf8',
use_unicode=True
Ví dụ
>>> db = MySQLdb.connect(host="localhost", user='root', passwd='passwd', db='sandbox', use_unicode=True, charset="utf8")
PostgreSQL
Thêm vào:
psycopg2.extensions.register_type(psycopg2.extensions.UNICODE)
psycopg2.extensions.register_type(psycopg2.extensions.UNICODEARRAY)
HTTP
Các trang web có thể được mã hóa trong bất kỳ mã hóa nào. Các Content-type
tiêu đề phải chứa một charset
lĩnh vực để ám chỉ mã hóa. Nội dung sau đó có thể được giải mã thủ công theo giá trị này. Ngoài ra, Python-Request trả về Unicodes response.text
.
Thủ công
Nếu bạn phải giải mã các chuỗi theo cách thủ công, bạn có thể chỉ cần thực hiện my_string.decode(encoding)
, encoding
mã hóa phù hợp ở đâu. Các codec được hỗ trợ Python 2.x được đưa ra ở đây: Mã hóa tiêu chuẩn . Một lần nữa, nếu bạn nhận được UnicodeDecodeError
thì có lẽ bạn đã mã hóa sai.
Thịt của bánh sandwich
Làm việc với Unicodes như bạn sẽ bình thường.
Đầu ra
xuất bản / in
print
viết qua dòng stdout. Python cố gắng định cấu hình bộ mã hóa trên thiết bị xuất chuẩn để Unicodes được mã hóa thành mã hóa của bàn điều khiển. Ví dụ: nếu shell của Linux locale
là en_GB.UTF-8
, đầu ra sẽ được mã hóa thành UTF-8
. Trên Windows, bạn sẽ bị giới hạn ở trang mã 8 bit.
Một bảng điều khiển được cấu hình không chính xác, chẳng hạn như ngôn ngữ bị hỏng, có thể dẫn đến các lỗi in không mong muốn. PYTHONIOENCODING
biến môi trường có thể buộc mã hóa cho thiết bị xuất chuẩn.
Các tập tin
Giống như đầu vào, io.open
có thể được sử dụng để chuyển đổi trong suốt Unicodes thành các chuỗi byte được mã hóa.
Cơ sở dữ liệu
Cấu hình tương tự để đọc sẽ cho phép Unicodes được viết trực tiếp.
Con trăn 3
Python 3 không có nhiều khả năng Unicode hơn Python 2.x, tuy nhiên nó hơi bị nhầm lẫn về chủ đề này. Ví dụ, thông thường str
bây giờ là một chuỗi Unicode và str
bây giờ là cũ bytes
.
Mã hóa mặc định là UTF-8, vì vậy nếu bạn .decode()
là một chuỗi byte mà không đưa ra mã hóa, Python 3 sử dụng mã hóa UTF-8. Điều này có thể khắc phục 50% các vấn đề Unicode của mọi người.
Hơn nữa, open()
hoạt động ở chế độ văn bản theo mặc định, do đó trả về giải mã str
(Unicode). Mã hóa được lấy từ ngôn ngữ của bạn, có xu hướng là UTF-8 trên các hệ thống Un * x hoặc trang mã 8 bit, chẳng hạn như windows-1251, trên các hộp Windows.
Tại sao bạn không nên sử dụng sys.setdefaultencoding('utf8')
Đó là một hack khó chịu (có một lý do bạn phải sử dụng reload
) sẽ chỉ che giấu các vấn đề và cản trở việc di chuyển của bạn sang Python 3.x. Hiểu vấn đề, khắc phục nguyên nhân gốc và thưởng thức Unicode zen. Xem tại sao chúng ta KHÔNG nên sử dụng sys.setdefaultencoding ("utf-8") trong tập lệnh py? để biết thêm chi tiết