55

这是我对错误消息的尝试。我究竟做错了什么?

string.decode("ascii", "ignore")

UnicodeEncodeError:“ascii”编解码器无法在位置 37 编码字符 u'\xa0':序数不在范围内(128)

string.encode('utf-8', "ignore")

UnicodeDecodeError:“ascii”编解码器无法解码位置 37 中的字节 0xc2:序数不在范围内(128)

4

4 回答 4

87

你不能解码 a unicode,也不能编码 a str试着反过来做。

于 2012-07-05T07:50:15.767 回答
61

猜测原始问题中省略的所有内容,但是,假设 Python 2.x 关键是仔细阅读错误消息:特别是在您调用“编码”但消息显示“解码”的地方,反之亦然,而且消息中包含的值的类型。

在第一个示例stringunicode,您尝试对其进行解码,这是一个将字节字符串转换unicode 的操作。Python 有用地尝试将 unicode 值转换为str使用默认的“ascii”编码,但由于您的字符串包含非 ascii 字符,因此您收到错误消息,表明 Python 无法编码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)
于 2012-07-05T11:02:38.573 回答
28

除了获取decodeencode倒退之外,我认为这里的部分答案实际上是不使用ascii编码。这可能不是你想要的。

首先,str将您想象成一个纯文本文件。它只是一堆实际上没有附加编码的字节。它的解释方式取决于读取它的任何代码。如果您不知道这一段在说什么,请阅读 Joel 的《每个软件开发人员绝对、肯定必须了解 Unicode 和字符集的绝对最低要求》,然后再继续阅读。

自然,我们都知道造成的混乱。答案是,至少在内存中,对所有字符串都有一个标准编码。这就是unicode进来的地方。我很难准确地跟踪 Python 内部使用的编码,但这并不重要。关键是您知道这是一个以某种方式解释的字节序列。所以你只需要考虑字符本身,而不是字节。

问题是,在实践中,你会同时遇到这两种情况。一些库给你一个str,而一些期望一个str. 当然,只要您正在流式传输一系列字节(例如到磁盘或从磁盘或通过 Web 请求),这都是有意义的。所以你需要能够来回翻译。

Enter codecs:是这两种数据类型之间的翻译库。用于从文本字符串 ( )encode生成字节序列( ),并用于从字节序列( )中获取文本字符串 ( )。strunicodedecodeunicodestr

例如:

>>> 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 一个字节序列,然后我告诉它,“给我这个的unicode版本,因为这个字节序列在'utf-8'.”中。它按照我的要求完成了,这些字节(一个心形字符)现在被视为一个整体,由它们的 Unicode 代码点表示。

让我们反过来:

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

'utf-8'我给 Python 一个 Unicode 字符串,并要求它使用编码将字符串转换为字节序列。确实如此,现在心脏只是一堆无法打印为 ASCII 的字节;所以它向我显示了十六进制。

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

>>> 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是什么。

  • 它来自一个文件吗?来自网络请求?从你的数据库?然后源确定编码。找出源的编码并使用它来将其翻译成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 does its best to make this immensely clearer simply by changing the names. In Python 3, str was replaced with bytes, and unicode was replaced with str.

于 2014-08-08T23:05:56.237 回答
2

那是因为你的输入字符串不能按照编码规则进行转换(默认是严格的)。

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

unicode(your_str, errors="ignore")
于 2012-07-05T07:58:02.163 回答