Python - ตัวแปลงสัญญาณ 'ascii' ไม่สามารถถอดรหัสไบต์ได้


119

ฉันสับสนจริงๆ ผมพยายามที่จะเข้ารหัส 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)

ฉันรู้วิธีหลีกเลี่ยงข้อผิดพลาดที่มีคำนำหน้า "u" ในสตริง ฉันแค่สงสัยว่าทำไมจึงเกิดข้อผิดพลาด "ไม่สามารถถอดรหัส" เมื่อมีการเรียกการเข้ารหัส Python กำลังทำอะไรอยู่ภายใต้ประทุน?

คำตอบ:


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

encodeแปลงวัตถุ Unicode เป็นstringวัตถุ แต่ที่นี่คุณได้เรียกใช้กับstringวัตถุ (เพราะคุณไม่มีคุณ) python จึงต้องแปลงวัตถุstringเป็นunicodeวัตถุก่อน มันจึงเทียบเท่ากับ

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

แต่การถอดรหัสล้มเหลวเนื่องจากสตริงไม่ถูกต้อง ascii นั่นเป็นสาเหตุที่คุณได้รับการร้องเรียนว่าไม่สามารถถอดรหัสได้


50
แล้วทางออกคืออะไร? โดยเฉพาะอย่างยิ่งถ้าฉันไม่มีสตริงลิเทอรัลฉันก็มีอ็อบเจกต์สตริง
Jon Tirsen

2
@JonTirsen คุณไม่ควรเข้ารหัสวัตถุสตริง อ็อบเจ็กต์สตริงถูกเข้ารหัสแล้ว หากคุณต้องการเปลี่ยนการเข้ารหัสคุณจะต้องถอดรหัสเป็นสตริงยูนิโคดจากนั้นจึงเข้ารหัสเป็นการเข้ารหัสที่ต้องการ
Winston Ewert

20
ดังนั้นเพื่อระบุให้ชัดเจนจากด้านบนคุณสามารถ"你好".decode('utf-8').encode('utf-8')
deinonychusaur

5
@WinstonEwert ฉันเดาว่าฉันสับสน ธุรกิจการเข้ารหัสมักจะทำให้ฉันสับสนตลอดไป ฉันเดาว่าความสับสนของฉันมาจากปัญหาของฉันเองที่ไม่รู้ว่าอินพุตเป็นสตริงหรือสตริงยูนิโคดหรือไม่และอาจมีการเข้ารหัสอะไร
deinonychusaur

@deinonychusaur ใช่ ... ฉันเข้าใจแล้ว
Winston Ewert

53

เสมอเข้ารหัสจาก Unicode ไบต์
ในทิศทางนี้คุณได้รับเลือกการเข้ารหัส

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

อีกวิธีหนึ่งคือการถอดรหัสจากไบต์เป็นยูนิโคด
ในทิศทางนี้คุณต้องรู้ว่าการเข้ารหัสคืออะไร

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

จุดนี้ไม่สามารถเน้นเพียงพอ หากคุณต้องการหลีกเลี่ยงการเล่น Unicode "whack-a-mole" สิ่งสำคัญคือต้องเข้าใจว่าเกิดอะไรขึ้นในระดับข้อมูล มีการอธิบายอีกวิธีหนึ่งดังนี้:

  • อ็อบเจ็กต์ Unicode ถูกถอดรหัสแล้วคุณไม่ต้องการเรียกdecodeใช้
  • มีการเข้ารหัสออบเจ็กต์ bytestring แล้วคุณไม่ต้องการเรียกencodeใช้

ตอนนี้เมื่อเห็น.encodeสตริงไบต์ Python 2 จะพยายามแปลงเป็นข้อความ ( unicodeวัตถุ) โดยปริยายก่อน ในทำนองเดียวกันเมื่อเห็น.decodeสตริงยูนิโคด Python 2 จะพยายามแปลงเป็นไบต์ ( strอ็อบเจกต์) โดยปริยาย

การแปลงโดยนัยเหล่านี้เป็นสาเหตุที่คุณจะได้รับเมื่อคุณโทรหา มันเป็นเพราะการเข้ารหัสมักจะยอมรับพารามิเตอร์ของประเภท; เมื่อได้รับพารามิเตอร์จะมีการถอดรหัสโดยนัยเป็นวัตถุประเภทก่อนที่จะเข้ารหัสใหม่ด้วยการเข้ารหัสอื่น การแปลงนี้เลือกตัวถอดรหัส 'ascii' เริ่มต้นซึ่งทำให้คุณมีข้อผิดพลาดในการถอดรหัสภายในตัวเข้ารหัสUnicodeDecodeErrorencodeunicodestrunicode

ในความเป็นจริงใน Python 3 มีวิธีการstr.decodeและbytes.encodeไม่มีอยู่จริง การลบออกเป็นความพยายาม [ขัดแย้ง] เพื่อหลีกเลี่ยงความสับสนนี้

... หรืออะไรก็ตามที่sys.getdefaultencoding()กล่าวถึงการเข้ารหัส; โดยปกติจะเป็น 'ascii'


คุณหมายความว่า Python ถอดรหัส bytestring ก่อนเข้ารหัสหรือไม่?
thoslin

@thoslin แน่นอนฉันได้เพิ่มรายละเอียดเพิ่มเติม
Wim

_ คืออะไรและเหตุใดข้อความพิมพ์ของคุณจึงไม่มีวงเล็บ
NoBugs

1
@NoBugs 1. ใน REPL _หมายถึงค่าก่อนหน้า 2. เพราะนี่คือคำถาม python-2.x
Wim

40

คุณสามารถลองสิ่งนี้

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

หรือ

คุณยังสามารถลองทำตาม

เพิ่มบรรทัดต่อไปนี้ที่ด้านบนของไฟล์. py ของคุณ

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

8

หากคุณใช้ Python <3 คุณจะต้องบอกล่ามว่าสตริงลิเทอรัลuของคุณคือ Unicode โดยนำหน้าด้วย :

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'

อ่านเพิ่มเติม : Unicode HOWTO


4
หากคุณกำลังเข้ารหัสสตริงเหตุใดจึงเกิดข้อผิดพลาดในการถอดรหัส
MxLDevs

3

คุณใช้u"你好".encode('utf8')ในการเข้ารหัสสตริงยูนิโคด แต่ถ้าคุณต้องการเป็นตัวแทน"你好"คุณควรถอดรหัส เหมือนกับ:

"你好".decode("utf8")

คุณจะได้รับสิ่งที่คุณต้องการ บางทีคุณควรเรียนรู้เพิ่มเติมเกี่ยวกับการเข้ารหัสและถอดรหัส


3

ในกรณีที่คุณกำลังจัดการกับ Unicode บางครั้งencode('utf-8')คุณสามารถลองเพิกเฉยต่ออักขระพิเศษเช่น

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

หรือเป็นที่แนะนำที่นี่something.decode('unicode_escape').encode('ascii','ignore')

ไม่มีประโยชน์อย่างยิ่งในตัวอย่างนี้ แต่สามารถทำงานได้ดีขึ้นในสถานการณ์อื่น ๆ เมื่อไม่สามารถแปลงอักขระพิเศษบางตัวได้

หรือคุณสามารถพิจารณาแทนที่ตัวละครโดยเฉพาะอย่างยิ่งการใช้replace()


1

หากคุณเริ่มต้นล่าม python จากเชลล์บน Linux หรือระบบที่คล้ายกัน (BSD ไม่แน่ใจเกี่ยวกับ Mac) คุณควรตรวจสอบการเข้ารหัสเริ่มต้นสำหรับเชลล์ด้วย

โทรlocale charmapจากเชลล์ (ไม่ใช่ตัวแปล python) และคุณจะเห็น

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

หากไม่ใช่กรณีนี้และคุณเห็นอย่างอื่นเช่น

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

Python จะ (อย่างน้อยในบางกรณีเช่นในของฉัน) รับช่วงการเข้ารหัสของเชลล์และจะไม่สามารถพิมพ์อักขระ Unicode (บางตัว? ทั้งหมด?) การเข้ารหัสเริ่มต้นของ Python ที่คุณเห็นและควบคุมผ่านsys.getdefaultencoding()และsys.setdefaultencoding()ในกรณีนี้จะถูกละเว้น

หากคุณพบว่าคุณมีปัญหานี้คุณสามารถแก้ไขได้โดย

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

(หรือเลือกคีย์แมปแบบใดก็ได้ที่คุณต้องการแทน en_EN) คุณยังสามารถแก้ไข/etc/locale.conf(หรือไฟล์ใดก็ตามที่ควบคุมนิยามโลแคลในระบบของคุณ) เพื่อแก้ไขสิ่งนี้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.