Python 异常消息的默认编码

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

下面的代码检查当输入非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 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.2

ASCII编码只包括使用您的代码片段进行实验得到的值为
的字节,看起来我在我的平台上也有相同的行为(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