以下代码检查了 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)"

我的问题: 为什么会出现错误信息 e[0] 用 Latin-1 编码? 默认编码是Ascii,这似乎是什么 unicode() 期望。

平台是Ubuntu 9.04,Python 2.6.2

有帮助吗?

解决方案

E [0]不与Latin-1的编码的;它只是恰巧字节\ XBD,当为Latin-1的解码时,是字符U + 00BD。

在转换发生在Objects/floatobject.c

首先,Unicode字符串必须被转换成一个字节串。此使用PyUnicode_EncodeDecimal()进行:

if (PyUnicode_EncodeDecimal(PyUnicode_AS_UNICODE(v),
                            PyUnicode_GET_SIZE(v),
                            s_buffer,
                            NULL))
        return NULL;

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

格式化错误的语句是:

PyOS_snprintf(buffer, sizeof(buffer),
              "invalid literal for float(): %.200s", s);

其中s包含先前创建的字节串。 PyOS_snprintf()写入一个字节串,和s是一个字节的字符串,所以它只是直接将其包括。

其他提示

很好的问题!

我冒昧挖成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;
    }

请参阅,它留下所有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

ASCII 编码仅包含具有值的字节 <= 127. 。在大多数编码中,这些字节表示的字符范围是相同的;换句话说,“A”是 chr(65) ASCII、latin-1、UTF-8 等。

然而,二分之一符号并不是 ASCII 字符集的一部分,因此当 Python 尝试将此符号编码为 ASCII 时,它除了失败之外什么也做不了。

更新: 发生的情况如下(我假设我们正在谈论 CPython):

float(u'\xbd') 导致 PyFloat_FromString浮动对象.c 被召唤。该函数给出一个 unicode 对象,依次调用 PyUnicode_EncodeDecimalunicodeobject.c 被召唤。通过浏览代码,我了解到该函数通过用 unicode 代码点替换每个字符来将 unicode 对象转换为字符串 <256 与该值的字节,即代码点 189 的半个字符被转换为 chr(89).

然后, PyFloat_FromString 像往常一样工作。此时,它正在使用常规字符串,该字符串恰好包含非 ASCII 范围字节。它不关心这个;它只是找到一个不是数字、句点等的字节,因此会引发值错误。

此异常的参数是一个字符串

"invalid literal for float(): " + evil_string

没关系;毕竟,异常消息是一个字符串。只有当您尝试使用默认编码 ASCII 解码该字符串时,才会出现问题。

从你的代码段尝试,它似乎我有我的平台上相同的行为(Py2.6在OS X 10.5)。

由于你确定,E [0]进行编码latin-1,正确的方法将其转换unicode是做.decode('latin-1'),和 unicode(e[0])

<强>更新因此它听起来像E [0]不具有有效的编码。 Definetely不latin-1。正因为如此,作为在评论中提到其他地方,你必须打电话repr(e[0])如果你需要显示此错误消息W / O引起级联例外。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top