为什么在使用str.decode()时,Python仍然会抛出UnicodeEncodeError?
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 个回答
你传入的字符串已经是unicode格式了。所以,在Python调用decode
之前,它必须先把这个字符串编码一下,而默认情况下,它会使用ASCII编码来处理。
补充说明 这要看你想做什么。如果你只是想把一个包含非ASCII字符的unicode字符串转换成HTML编码的格式,你可以用一句话做到:text.encode('ascii', 'xmlcharrefreplace')
。
这个错误报告有点误导,因为它跟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
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,让它自己搞定编码问题。