tl; dr / การแก้ไขด่วน
- อย่าถอดรหัส / เข้ารหัสโดยเจตนา
- อย่าถือว่าสตริงของคุณเข้ารหัส UTF-8
- ลองแปลงสตริงเป็นสตริง Unicode โดยเร็วที่สุดในรหัสของคุณ
- แก้ไขสถานที่ของคุณ: วิธีแก้ UnicodeDecodeError ใน Python 3.6?
- อย่าถูกล่อลวงให้ใช้
reload
แฮ็คด่วน
Unicode Zen ใน Python 2.x - รุ่นยาว
การไม่เห็นแหล่งที่มาเป็นการยากที่จะทราบสาเหตุที่แท้จริงดังนั้นฉันจะต้องพูดโดยทั่วไป
UnicodeDecodeError: 'ascii' codec can't decode byte
โดยทั่วไปจะเกิดขึ้นเมื่อคุณพยายามแปลง Python 2.x str
ที่มีไม่ใช่ ASCII เป็นสตริง Unicode โดยไม่ต้องระบุการเข้ารหัสของสตริงต้นฉบับ
โดยสังเขปสตริง Unicode เป็นสตริง Python ที่แยกจากกันอย่างสิ้นเชิงซึ่งไม่มีการเข้ารหัสใด ๆ พวกเขาเก็บรหัสจุด Unicode เท่านั้นดังนั้นจึงสามารถเก็บจุด Unicode ใด ๆ ได้จากทั่วทั้งสเปกตรัม Strings มีข้อความที่เข้ารหัสเลนซา UTF-8, UTF-16, ISO-8895-1, GBK, Big5 ฯลฯสตริงจะถูกถอดรหัสเป็น UnicodeและUnicodes จะถูกเข้ารหัสสตริง ไฟล์และข้อมูลข้อความจะถูกถ่ายโอนในสตริงที่เข้ารหัสเสมอ
ผู้เขียนโมดูล Markdown อาจใช้unicode()
(โดยมีข้อยกเว้นเกิดขึ้น) เป็นเกตคุณภาพไปยังโค้ดที่เหลือ - มันจะแปลง ASCII หรือห่อสตริง Unicode ที่มีอยู่แล้วเป็นสตริง Unicode ใหม่ ผู้เขียน Markdown ไม่สามารถรู้การเข้ารหัสของสตริงที่เข้ามาดังนั้นจะพึ่งพาคุณในการถอดรหัสสตริงเป็นสตริง Unicode ก่อนที่จะส่งไปยัง Markdown
สตริง Unicode สามารถประกาศในรหัสของคุณโดยใช้u
คำนำหน้าเพื่อสตริง เช่น
>>> my_u = u'my ünicôdé strįng'
>>> type(my_u)
<type 'unicode'>
สตริง Unicode อาจมาจากไฟล์ฐานข้อมูลและโมดูลเครือข่าย เมื่อสิ่งนี้เกิดขึ้นคุณไม่จำเป็นต้องกังวลเกี่ยวกับการเข้ารหัส
gotchas
การแปลงจากstr
เป็น Unicode unicode()
สามารถเกิดขึ้นได้แม้ในขณะที่คุณไม่ชัดเจนโทร
สถานการณ์สมมติต่อไปนี้ทำให้เกิดUnicodeDecodeError
ข้อยกเว้น:
# 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: ' + '€'
ตัวอย่าง
ในแผนภาพต่อไปนี้คุณสามารถดูวิธีcafé
การเข้ารหัสในการเข้ารหัส "UTF-8" หรือ "Cp1252" ขึ้นอยู่กับประเภทของเทอร์มินัล ในทั้งสองตัวอย่างcaf
เป็นเพียง ascii ปกติ ใน UTF-8 é
ถูกเข้ารหัสโดยใช้สองไบต์ ใน "Cp1252" éคือ 0xE9 (ซึ่งเกิดขึ้นกับค่าจุด Unicode ด้วย (ไม่ใช่เรื่องบังเอิญ)) ความถูกต้องถูกdecode()
เรียกใช้และการแปลงเป็น Python Unicode นั้นสำเร็จแล้ว:
ในแผนภาพนี้decode()
ถูกเรียกด้วยascii
(ซึ่งเหมือนกับการโทรunicode()
โดยไม่ต้องเข้ารหัส) เนื่องจาก ASCII ไม่สามารถมีจำนวนไบต์ที่มากกว่า0x7F
นี้จะทำให้เกิดUnicodeDecodeError
ข้อยกเว้น:
แซนด์วิช Unicode
เป็นวิธีปฏิบัติที่ดีในการสร้างแซนวิช Unicode ในรหัสของคุณซึ่งคุณถอดรหัสข้อมูลขาเข้าทั้งหมดไปยังสตริง Unicode ทำงานกับ Unicodes จากนั้นเข้ารหัสstr
เป็นขาออก สิ่งนี้จะช่วยให้คุณไม่ต้องกังวลเกี่ยวกับการเข้ารหัสสตริงที่อยู่ตรงกลางโค้ดของคุณ
อินพุต / ถอดรหัส
รหัสแหล่งที่มา
หากคุณต้องการที่จะอบไม่ใช่ ASCII เป็นรหัสที่มาของคุณเพียงแค่สร้างสตริง Unicode โดย prefixing u
สตริงที่มี เช่น
u'Zürich'
ในการอนุญาตให้ Python ถอดรหัสซอร์สโค้ดของคุณคุณจะต้องเพิ่มส่วนหัวการเข้ารหัสเพื่อให้ตรงกับการเข้ารหัสไฟล์ของคุณ ตัวอย่างเช่นหากไฟล์ของคุณถูกเข้ารหัสเป็น 'UTF-8' คุณจะใช้:
# encoding: utf-8
นี้เป็นสิ่งจำเป็นเฉพาะเมื่อคุณมีไม่ใช่ ASCII ของคุณในรหัสที่มา
ไฟล์
โดยปกติจะได้รับข้อมูลที่ไม่ใช่ ASCII จากไฟล์ io
โมดูลให้ TextWrapper encoding
ที่ถอดรหัสไฟล์ของคุณได้ทันทีโดยใช้ที่กำหนด คุณต้องใช้การเข้ารหัสที่ถูกต้องสำหรับไฟล์ - ไม่สามารถเดาได้ง่าย ตัวอย่างเช่นสำหรับไฟล์ 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
จะเหมาะสำหรับการส่งผ่านไปยัง Markdown หาก a UnicodeDecodeError
จากread()
บรรทัดแสดงว่าคุณอาจใช้ค่าการเข้ารหัสผิด
ไฟล์ CSV
โมดูล Python 2.7 CSV ไม่สนับสนุนอักขระที่ไม่ใช่ ASCII 😩 ความช่วยเหลือที่อยู่ในมืออย่างไรกับhttps://pypi.python.org/pypi/backports.csv
ใช้เหมือนข้างบน แต่ส่งไฟล์ที่เปิดไป:
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
ฐานข้อมูล
ไดรเวอร์ฐานข้อมูล Python ส่วนใหญ่สามารถส่งคืนข้อมูลใน Unicode แต่โดยปกติจะต้องมีการกำหนดค่าเล็กน้อย ใช้สตริง Unicode สำหรับแบบสอบถาม SQL เสมอ
MySQL
ในสตริงการเชื่อมต่อเพิ่ม:
charset='utf8',
use_unicode=True
เช่น
>>> db = MySQLdb.connect(host="localhost", user='root', passwd='passwd', db='sandbox', use_unicode=True, charset="utf8")
PostgreSQL
เพิ่ม:
psycopg2.extensions.register_type(psycopg2.extensions.UNICODE)
psycopg2.extensions.register_type(psycopg2.extensions.UNICODEARRAY)
HTTP
หน้าเว็บสามารถเข้ารหัสในการเข้ารหัสใด ๆ Content-type
หัวควรมีcharset
ข้อมูลแบะท่าเข้ารหัส เนื้อหาสามารถถอดรหัสด้วยตนเองเทียบกับค่านี้ อีกวิธีหนึ่งคืองูหลามร้องขอresponse.text
ผลตอบแทนใน Unicodes
ด้วยมือ
หากคุณต้องถอดรหัสสตริงด้วยตนเองคุณสามารถทำได้my_string.decode(encoding)
โดยencoding
การเข้ารหัสที่เหมาะสม งูหลาม 2.x สนับสนุนตัวแปลงสัญญาณที่จะได้รับที่นี่: มาตรฐานการเข้ารหัส อีกครั้งถ้าคุณได้UnicodeDecodeError
แล้วคุณอาจมีการเข้ารหัสผิด
เนื้อของแซนวิช
ทำงานกับ Unicodes ได้เหมือนปกติ
เอาท์พุต
stdout / การพิมพ์
print
เขียนผ่านสตรีม stdout Python พยายามกำหนดค่าตัวเข้ารหัสบน stdout เพื่อให้ Unicodes ถูกเข้ารหัสเป็นการเข้ารหัสของคอนโซล ตัวอย่างเช่นถ้าลินุกซ์ของเปลือกlocale
มีเอาท์พุทจะถูกเข้ารหัสเพื่อen_GB.UTF-8
UTF-8
บน Windows คุณจะถูก จำกัด ที่หน้ารหัส 8 บิต
คอนโซลที่กำหนดค่าไม่ถูกต้องเช่นภาษาที่เสียหายสามารถนำไปสู่ข้อผิดพลาดการพิมพ์ที่ไม่คาดคิด PYTHONIOENCODING
ตัวแปรสภาพแวดล้อมสามารถบังคับให้เข้ารหัสสำหรับ stdout
ไฟล์
เช่นเดียวกับอินพุตio.open
สามารถใช้ในการแปลง Unicode เป็นสตริงไบต์ที่เข้ารหัส
ฐานข้อมูล
การกำหนดค่าเดียวกันสำหรับการอ่านจะช่วยให้ Unicodes สามารถเขียนได้โดยตรง
Python 3
Python 3 ไม่สามารถใช้ Unicode ได้มากกว่า Python 2.x อย่างไรก็ตามมันค่อนข้างสับสนน้อยกว่าในหัวข้อ เช่นปกติstr
ในขณะนี้คือสตริง Unicode และเก่าอยู่ในขณะนี้str
bytes
การเข้ารหัสเริ่มต้นคือ UTF-8 ดังนั้นหากคุณ.decode()
เป็นสตริงไบต์โดยไม่มีการเข้ารหัส Python 3 ใช้การเข้ารหัส UTF-8 สิ่งนี้อาจแก้ไขปัญหา Unicode ของผู้คนได้ 50%
ยิ่งไปกว่านั้นopen()
ทำงานในโหมดข้อความตามค่าเริ่มต้นดังนั้นจะส่งกลับค่าถอดรหัสstr
(Unicode) การเข้ารหัสมาจากภาษาของคุณซึ่งมีแนวโน้มที่จะเป็น UTF-8 ในระบบ Un * x หรือหน้ารหัส 8 บิตเช่น windows-1251 บนกล่อง Windows
ทำไมคุณไม่ควรใช้ sys.setdefaultencoding('utf8')
เป็นแฮ็คที่น่ารังเกียจ (มีเหตุผลที่คุณต้องใช้reload
) ซึ่งจะปกปิดปัญหาและขัดขวางการย้ายไปยัง Python 3.x ทำความเข้าใจกับปัญหาแก้ไขสาเหตุที่แท้จริงและเพลิดเพลินกับ Unicode Zen ดูทำไมเราไม่ควรใช้ sys.setdefaultencoding ("utf-8") ในสคริปต์ py? สำหรับรายละเอียดเพิ่มเติม