Python 异常消息的默认编码
下面的代码检查当输入非ascii符号时Python 异常消息的默认编码,python,exception,encoding,python-2.x,Python,Exception,Encoding,Python 2.x,下面的代码检查当输入非ascii符号时float()方法的行为: import sys try: float(u'\xbd') except ValueError as e: print sys.getdefaultencoding() # in my system, this is 'ascii' print e[0].decode('latin-1') # u'invalid literal for float(): ' followed by the 1/2 (one hal
float()
方法的行为:
import sys
try:
float(u'\xbd')
except ValueError as e:
print sys.getdefaultencoding() # in my system, this is 'ascii'
print e[0].decode('latin-1') # u'invalid literal for float(): ' followed by the 1/2 (one half) character
print unicode(e[0]) # raises "UnicodeDecodeError: 'ascii' codec can't decode byte 0xbd in position 29: ordinal not in range(128)"
我的问题:为什么错误消息e[0]
用拉丁语1编码?默认编码是Ascii,这似乎是unicode()
所期望的
平台是Ubuntu9.04,Python2.6.2ASCII编码只包括使用您的代码片段进行实验得到的值为
的字节,看起来我在我的平台上也有相同的行为(OS X 10.5上的Py2.6)
由于您确定e[0]是用latin-1
编码的,因此转换它的正确方法是执行.decode('latin-1')
,而不是unicode(e[0])
更新:所以听起来e[0]没有有效的编码。肯定不是拉丁语-1
。因此,如评论中其他地方所述,如果需要显示此错误消息而不导致级联异常,则必须调用repr(e[0])
。e[0]未使用拉丁语-1编码;碰巧的是,字节\xbd在解码为拉丁语-1时是字符U+00BD
转换发生在对象/floatobject.c
中
首先,必须将unicode字符串转换为字节字符串。这是使用PyUnicode\u EncodeDecimal()
执行的:
它在unicodeobject.c
中实现。它不执行任何类型的字符集转换,只写入值等于字符串的unicode序号的字节。在这种情况下,U+00BD->0xBD
设置错误格式的语句是:
PyOS_snprintf(buffer, sizeof(buffer),
"invalid literal for float(): %.200s", s);
其中s
包含先前创建的字节字符串PyOS\u snprintf()
写一个字节字符串,而s
是一个字节字符串,所以它直接包含它。问得好
我冒昧地深入研究了Python的源代码,这在正确设置的linux发行版上仅仅是一个命令(apt get source python2.5
)
该死,约翰·米利金比我先到。没错,PyUnicode\u EncodeDecimal
就是答案,它在这里执行以下操作:
/* (Loop ch in the unicode string) */
if (Py_UNICODE_ISSPACE(ch)) {
*output++ = ' ';
++p;
continue;
}
decimal = Py_UNICODE_TODECIMAL(ch);
if (decimal >= 0) {
*output++ = '0' + decimal;
++p;
continue;
}
if (0 < ch && ch < 256) {
*output++ = (char)ch;
++p;
continue;
}
/* All other characters are considered unencodable */
collstart = p;
collend = p+1;
while (collend < end) {
if ((0 < *collend && *collend < 256) ||
!Py_UNICODE_ISSPACE(*collend) ||
Py_UNICODE_TODECIMAL(*collend))
break;
}
/*(unicode字符串中的循环ch)*/
if(Py_UNICODE_ISSPACE(ch)){
*输出+='';
++p;
继续;
}
十进制=Py_UNICODE_TODECIMAL(ch);
如果(十进制>=0){
*输出+++=“0”+小数;
++p;
继续;
}
if(0
请参见,基于unicode的向后兼容性,它保留了所有小于256的unicode代码点,即拉丁字符-1
附录
有了这个选项,您可以通过尝试其他非拉丁-1字符进行验证,它将引发不同的异常:
>>> float(u"ħ")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'decimal' codec can't encode character u'\u0127' in position 0: invalid decimal Unicode string
浮动(u“ħ”)
回溯(最近一次呼叫最后一次):
文件“”,第1行,在
UnicodeEncodeError:“十进制”编解码器无法对位置0中的字符u'\u0127'进行编码:十进制Unicode字符串无效
恐怕这还不能回答我的问题。为了更加清晰,我对原文稍加修改。(我把问题改成粗体)。我理解Ascii不能为奇怪的字符提供表示。我的问题是e[0]似乎是用拉丁语1编码的,尽管Ascii是默认编码。我的理由是float()应该引发Ascii编码的异常(或Unicode)。然而,它是拉丁语-1或类似的。它应该尝试用Ascii编码错误消息,但失败了,首先引发的是unicode DecodeError,而不是ValueError。这应该是chr(189)
我猜它不是拉丁-1编码,它只是将unicode序号值转换为字节。这在Python中应该被视为错误吗?我的推理是:如果float()收到一个Unicode字符串,如果消息将包含输入,它应该抛出一个Unicode描述的异常。否则,异常无法安全处理,如示例所示。我认为将其称为bug是公平的——错误消息应该包含repr(v)
,而不是str(s)
,因为知道原始输入值比十进制编码版本更有用。
/* (Loop ch in the unicode string) */
if (Py_UNICODE_ISSPACE(ch)) {
*output++ = ' ';
++p;
continue;
}
decimal = Py_UNICODE_TODECIMAL(ch);
if (decimal >= 0) {
*output++ = '0' + decimal;
++p;
continue;
}
if (0 < ch && ch < 256) {
*output++ = (char)ch;
++p;
continue;
}
/* All other characters are considered unencodable */
collstart = p;
collend = p+1;
while (collend < end) {
if ((0 < *collend && *collend < 256) ||
!Py_UNICODE_ISSPACE(*collend) ||
Py_UNICODE_TODECIMAL(*collend))
break;
}
>>> float(u"ħ")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'decimal' codec can't encode character u'\u0127' in position 0: invalid decimal Unicode string