字符串编码和解码?

55

以下是我的尝试及错误信息,请问我做错了什么?

string.decode("ascii", "ignore")
UnicodeEncodeError: 'ascii'编解码器无法将字符u'\xa0'编码到位置37:序数不在范围内(128)。
string.encode('utf-8', "ignore")
UnicodeDecodeError: 'ascii'编解码器无法解码位置为37的0xc2字节:该序数不在128的范围内。

string 的值是什么?它是什么类型? - Marco de Wit
对Unicode对象进行解码是没有意义的,因为它已经处于解码状态。当您调用unicode_object.decode()时,Python会假定您想将字节字符串解码为Unicode。它首先尝试使用系统的默认编码将Unicode对象编码为字节字符串 - 这就是您看到的真正错误。 - kumar303
4个回答

87

你不能解码一个 unicode,也不能编码一个 str。试着反过来做 就可以了


8
好的,我会尽力进行翻译。请提供需要翻译的原文。 - Duncan
8
我是唯一一个认为Python这样做有问题的人吗?当我将Python字符串转换为其二进制UTF-8表示时,应该称之为“编码”,而不是反过来吧? - John B
1
@rogueprocess Python与您所描述的相反:u"\u2603".encode('utf8')返回UTF-8表示中的字节字符串,而"\xe2\x98\x83".decode('utf8')返回Unicode字符串。 - ojrac
1
那是一份很棒的演示,也是我迄今为止看过的最清晰的说明。谢谢你分享这个链接。 - culix
@rogueprocess:“将Unicode对象转换为字节序列称为编码,从字节序列重新创建Unicode对象称为解码。” https://docs.python.org/2/library/codecs.html#codec-base-classes - Bennett Brown
显示剩余2条评论

61

不知道原问题中省略了什么,但是假设使用的是Python2.x,关键是要仔细阅读错误信息:特别是当你调用“encode”而消息显示“decode”,或者反之,还有包含在消息中的值的类型。

在第一个例子中,“string”的类型是“unicode”,而您尝试对其进行解码操作,这是将字节字符串转换为Unicode的操作。 Python会尝试使用默认的“ascii”编码将Unicode值转换为“str”,但由于您的字符串包含非ASCII字符,因此会出现错误,该错误指出无法对Unicode值进行编码。以下是一个示例,显示了输入字符串的类型:

>>> u"\xa0".decode("ascii", "ignore")

Traceback (most recent call last):
  File "<pyshell#7>", line 1, in <module>
    u"\xa0".decode("ascii", "ignore")
UnicodeEncodeError: 'ascii' codec can't encode character u'\xa0' in position 0: ordinal not in range(128)

在第二种情况下,您尝试对字节字符串进行反向编码。编码是一种将Unicode转换为字节字符串的操作,因此Python会帮助您尝试首先将字节字符串转换为Unicode,但由于您没有提供ASCII字符串,所以默认的ASCII解码器失败了。
>>> "\xc2".encode("ascii", "ignore")

Traceback (most recent call last):
  File "<pyshell#6>", line 1, in <module>
    "\xc2".encode("ascii", "ignore")
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc2 in position 0: ordinal not in range(128)

这解释了这个神话。 - foresightyj

28

除了将decodeencode搞反之外,我认为这里的答案部分应该是不要使用 ascii 编码。这可能不是你想要的。

首先,把str看作你会处理纯文本文件一样。它只是一堆没有实际编码的字节。它的解释方式取决于读取它的代码片段。如果你不知道这段话在说什么,请立即阅读Joel的《每个软件开发人员绝对必须了解的 Unicode 和字符集最低限度》再继续下去。

当然,我们都知道这可能会造成混乱。解决方案是:至少在内存中,所有字符串都采用标准编码。这就是unicode发挥作用的地方。我很难跟踪Python内部确切使用的编码,但就目前而言,这并不重要。关键是你知道它是一系列以某种方式解释的字节。因此,你只需要考虑字符本身,而不是字节。

问题在于,在实践中,你会遇到两者并存的情况。有些库提供给你一个str,有些则期望一个str。当你流式传输一系列字节(例如从磁盘或通过网络请求传输)时,这当然是有意义的。所以你需要能够进行相互转换。

这就是codecs的作用:它是这两种数据类型之间的转换库。你可以使用 encode 从文本字符串 (unicode) 生成一个字节序列 (str),并使用 decode 从字节序列 (str) 获取一个文本字符串 (unicode)。

例如:

>>> s = "I look like a string, but I'm actually a sequence of bytes. \xe2\x9d\xa4"
>>> codecs.decode(s, 'utf-8')
u"I look like a string, but I'm actually a sequence of bytes. \u2764"

发生了什么事情?我向Python提供了一系列字节,然后告诉它:“在假定这串字节是使用'utf-8'编码的前提下,给我它们的unicode版本。” 它按照我的要求做了,那些字节(一个心形字符)现在被视为一个整体,并用它们的Unicode代码点表示。

我们换个方向:

>>> u = u"I'm a string! Really! \u2764"
>>> codecs.encode(u, 'utf-8')
"I'm a string! Really! \xe2\x9d\xa4"

我给Python一个Unicode字符串,要求它使用"utf-8"编码将该字符串转换为字节序列。 Python完成了这个任务,但现在的心形符号只是一堆无法作为ASCII字符打印出来的字节,所以Python显示了十六进制。

当然,我们也可以使用其他编码方式:

>>> s = "I have a section \xa7"
>>> codecs.decode(s, 'latin1')
u'I have a section \xa7'
>>> codecs.decode(s, 'latin1')[-1] == u'\u00A7'
True

>>> u = u"I have a section \u00a7"
>>> u
u'I have a section \xa7'
>>> codecs.encode(u, 'latin1')
'I have a section \xa7'

('\xa7' 是 Unicode 和 Latin-1 中的 章节符号)。

所以对于您的问题,首先需要确定您的 str 使用的编码方式。

  • 它是来自文件?来自 Web 请求?来自数据库?然后源确定编码方式。找出源的编码方式,并使用该编码方式将其转换为 unicode

s = [get from external source]
u = codecs.decode(s, 'utf-8') # Replace utf-8 with the actual input encoding
  • 或者你正在尝试将其写在某个地方。目标使用哪种编码方式?使用该编码方式将其转换为str。UTF-8是普通文本文档的一个不错选择;大多数东西都可以读取它。

  • u = u'My string'
    s = codecs.encode(u, 'utf-8') # Replace utf-8 with the actual output encoding
    [Write s out somewhere]
    
  • 你只是为了互操作性而在内存中来回翻译吗?那么只需选择一种编码并坚持使用它;'utf-8' 可能是最好的选择:

  • u = u'My string'
    s = codecs.encode(u, 'utf-8')
    newu = codecs.decode(s, 'utf-8')
    

    在现代编程中,你可能永远不想使用 'ascii' 编码。这是所有可能字符的一个非常小的子集,并且我所知道的没有任何系统默认使用它。

    Python 3通过更改名称尽力使这一点变得极其清晰。在Python 3中,str 被替换为 bytes,而 unicode 被替换为 str


    对于任何字节串x和任何解码不会失败(没有UnicodeDecodeError)的编码,x.decode(some_encoding).encode(some_encoding) == x是否为真?(使用py3符号而不是codecs,type(x) == bytes - Mr_and_Mrs_D

    2

    这是因为您的输入字符串无法根据编码规则进行转换(默认情况下是严格的)。

    我不知道,但我总是直接使用unicode()构造函数进行编码,至少在官方文档中是这样的:

    unicode(your_str, errors="ignore")
    

    谢谢,这对我很有帮助。 - ashim888
    3
    这将从字符串中删除非ASCII字符。(unicode("\xe2\x9d\xa4", errors='ignore')会得到u''.) 如果这是可以接受的结果,那么这可能是可以的。虽然在大多数情况下丢失数据都不可接受,但至少此答案需要详细说明这样做的适当性。 - jpmc26

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接