异常消息的默认编码

pab*_*obm 6 python encoding exception python-2.x

以下代码检查了float()在输入非ascii符号时方法的行为:

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)"
Run Code Online (Sandbox Code Playgroud)

我的问题:为什么错误信息e[0]以Latin-1编码?默认编码是Ascii,这似乎是unicode()预期的.

平台是Ubuntu 9.04,Python 2.6.2

Joh*_*kin 8

e [0]不用latin-1编码; 事实上,当解码为latin-1时,字节\ xbd是字符U + 00BD.

转换发生在Objects/floatobject.c.

首先,必须将unicode字符串转换为字节字符串.使用PyUnicode_EncodeDecimal()以下方式执行:

if (PyUnicode_EncodeDecimal(PyUnicode_AS_UNICODE(v),
                            PyUnicode_GET_SIZE(v),
                            s_buffer,
                            NULL))
        return NULL;
Run Code Online (Sandbox Code Playgroud)

这是实现的unicodeobject.c.它不执行任何类型的字符集转换,它只是写入值等于字符串的unicode序数的字节.在这种情况下,U + 00BD - > 0xBD.

格式化错误的语句是:

PyOS_snprintf(buffer, sizeof(buffer),
              "invalid literal for float(): %.200s", s);
Run Code Online (Sandbox Code Playgroud)

其中s包含先前创建的字节字符串.PyOS_snprintf()写一个字节字符串,并且s是一个字节字符串,所以它只是直接包含它.

  • 我认为把它称为bug是公平的 - 错误的messaeg应该包含`repr(v)`而不是`str(s)`,因为知道原始输入值比十进制编码版本更有用. (3认同)

u0b*_*6ae 5

非常好的问题!

我冒昧地深入研究Python的源代码,这仅仅是正确设置linux发行版的命令(apt-get source python2.5)

该死的,约翰米利金打败了我.这是正确的,这PyUnicode_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;
    }
Run Code Online (Sandbox Code Playgroud)

看,根据Unicode的向后兼容性,它保留了所有unicode代码点<256,即latin-1字符.


附录

有了这个,您可以通过尝试其他非latin-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
Run Code Online (Sandbox Code Playgroud)