为什么在使用str.decode()时,Python仍然会抛出UnicodeEncodeError?

4 投票
6 回答
10578 浏览
提问于 2025-04-17 08:47
UnicodeEncodeError: 'ascii' codec can't encode character u'\xe1' in position 0: ordinal not in range(128)

考虑这个函数:

def escape(text):
    print repr(text)
    escaped_chars = []
    for c in text:
        try:
            c = c.decode('ascii')
        except UnicodeDecodeError:
            c = '&{};'.format(htmlentitydefs.codepoint2name[ord(c)])
        escaped_chars.append(c)
    return ''.join(escaped_chars)

它应该把所有非ASCII字符转换成对应的HTML实体。不过不幸的是,当变量 text 包含字符串 repr() 的结果是 u'Tam\xe1s Horv\xe1th' 时,Python会报错。

但是,我并没有使用 str.encode()。我只使用了 str.decode()。我是不是漏掉了什么?

6 个回答

5

你传入的字符串已经是unicode格式了。所以,在Python调用decode之前,它必须先把这个字符串编码一下,而默认情况下,它会使用ASCII编码来处理。

补充说明 这要看你想做什么。如果你只是想把一个包含非ASCII字符的unicode字符串转换成HTML编码的格式,你可以用一句话做到:text.encode('ascii', 'xmlcharrefreplace')

11

这个错误报告有点误导,因为它跟Python处理编码和解码的方式有关。你试图对一个已经解码过的字符串再进行一次解码,这让Python的函数感到困惑,结果它也让你感到困惑!😉 编码和解码的过程,按照我所知道的,是通过codecs模块来完成的。而这个误导性错误信息的根源就藏在这里。

你可以自己检查一下:要么

u'\x80'.encode('ascii')

要么

u'\x80'.decode('ascii')

会抛出一个UnicodeEncodeError,而

u'\x80'.encode('utf8')

则不会,但

u'\x80'.decode('utf8')

又会抛出错误!

我想你可能对编码和解码的意思感到困惑。简单来说:

                     decode             encode    
ByteString (ascii)  --------> UNICODE  --------->  ByteString (utf8)
            codec                                              codec

那么,为什么在decode方法中会有一个codec参数呢?因为底层的函数无法猜测这个字节字符串是用什么编码的,所以它需要你提供codec作为提示。如果你不提供,它会默认使用sys.getdefaultencoding()

所以当你使用c.decode('ascii')时,你实际上是 a) 有一个(已编码的)字节字符串(这就是你要解码的原因),b) 你想得到一个Unicode表示对象(这就是你使用解码的目的),c) 这个字节字符串是用ascii编码的。

另外可以参考: https://stackoverflow.com/a/370199/1107807
http://docs.python.org/howto/unicode.html
http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
http://www.stereoplex.com/blog/python-unicode-and-unicodedecodeerror

2

Python 有两种类型的字符串:字符字符串(unicode 类型)和字节字符串(str 类型)。你粘贴的代码是针对字节字符串的。你需要一个类似的函数来处理字符字符串。

也许可以这样做:

def uescape(text):
    print repr(text)
    escaped_chars = []
    for c in text:
        if (ord(c) < 32) or (ord(c) > 126):
            c = '&{};'.format(htmlentitydefs.codepoint2name[ord(c)])
        escaped_chars.append(c)
    return ''.join(escaped_chars)

我在想,这两个函数对你来说真的有必要吗?如果是我,我会选择 UTF-8 作为结果文档的字符编码,直接用字符字符串的形式处理文档(不用担心实体问题),最后一步再执行 content.encode('UTF-8'),然后再把它交给客户端。根据你选择的网络框架,你甚至可以直接把字符字符串送到 API,让它自己搞定编码问题。

撰写回答