为什么在format字符串中使用warnings.formatwarning会出现编码错误?

5 投票
3 回答
7578 浏览
提问于 2025-04-15 14:51

我在这一行代码上遇到了编码错误:

s =  "%s:%s: %s: %s\n" % (filename, lineno, category.__name__, message)

错误信息是:UnicodeEncodeError: 'ascii' 编码无法在位置 44 编码字符 u'\xc4':序号不在范围内(128)

我尝试通过传递所有参数组合来重现这个错误,但我得到的最接近的错误是“ascii 解码”错误(这是因为同时传递了 unicode 和高 ascii 字符串,导致字符串被强制转换为 unicode,使用了 ascii 解码器)。

不过,我没有成功得到“ascii 编码”错误。有人知道这是怎么回事吗?

3 个回答

1

你传递的其中一个操作数不适合ASCII编码——可能它包含了Unicode或Latin-1字符。试着把格式字符串改成Unicode,看看会发生什么。

8

你在混用unicode和str对象。

解释一下: 在Python 2.x中,有两种可以包含文本字符串的对象:str和unicode。str是字节字符串,所以它只能包含0到255之间的字符。unicode则是包含unicode字符的字符串。

你可以通过“encode”和“decode”方法在str和unicode之间转换:

>>> "thisisastring".decode('ascii')
u'thisisastring'

>>> u"This is ä string".encode('utf8')    
'This is \xc3\xa4 string'

注意编码。编码是将unicode文本表示为字节字符串的方式。

如果你尝试把str和unicode加在一起,Python会试图把其中一个转换成另一个。但默认情况下,它会使用ASCII作为编码,这意味着只能包含a-z、A-Z和一些额外的字符,比如!"#$%&/()=?'{[]]}等。其他的字符会导致错误。

在这种情况下,你会遇到编码错误或解码错误,这取决于Python是试图把unicode转换成str,还是把str转换成unicode。通常情况下,它会尝试解码,也就是转换成unicode。但有时它会选择不这样做,而是强制转换成字符串。我也不太清楚为什么。

更新: 你上面遇到编码错误而不是解码错误的原因是,message在上面的代码中既不是str也不是unicode。它是另一个对象,具有str方法。因此,Python在传递之前会先执行str(message),但这会失败,因为内部存储的message是一个无法转换为ascii的unicode对象。

或者,更简单地说:失败是因为warnings.warn()不接受unicode消息。

现在,解决方案:

不要混用str和unicode。如果你需要使用unicode,显然是这样的话,尽量确保所有字符串始终都是unicode。这样才能确保避免这个问题。这意味着每当你从磁盘读取字符串,或者调用一个可能返回非纯ascii str的函数时,尽快将其解码为unicode。 当你需要将其保存到磁盘、通过网络发送,或者传递给不理解unicode的方法时,尽量晚一点再编码为str。

在这个特定的情况下,问题是你把unicode传递给了warnings.warn(),这是不可以的。你应该传递一个字符串。如果你不知道它是什么(这里似乎就是这种情况),因为它来自其他地方,你的try/except解决方案和repr方法都可以正常工作,虽然进行编码也是一个可能的选择。

8

这个问题发生在Python试图转换一个参数的时候:

s = u"\u00fc"
print str(s)
UnicodeEncodeError: 'ascii' codec can't encode character u'\xfc' in position 0: ordinal not in range(128)

之所以会出现这个问题,是因为你的某个参数是一个对象(而不是任何类型的字符串),Python会对它调用 str() 函数。解决这个问题有两个办法:使用一个Unicode字符串来格式化(比如 s = u"%s..."),或者用 repr() 把每个参数包裹起来。

撰写回答