虽然有个一般性的 UnicodeError
异常,但是报告错误时几乎都会指明具体的异常:UnicodeEncodeError
(把字符串转换成二进制序列时)或 UnicodeDecodeError
(把二进制序列转换成字符串时)。如果源码的编码与预期不符,加载 Python 模块时还可能抛出 SyntaxError
。接下来说明如何处理这些错误。
1.处理UnicodeEncodeError
多数非 UTF 编解码器只能处理 Unicode
字符的一小部分子集。把文本转换成字节序列时,如果目标编码中没有定义某个字符,那就会抛出 UnicodeEncodeError
异常,除非把 errors
参数传给编码方法或函数,对错误进行特殊处理。下面是用例子进行演示。
>>> city = 'São Paulo'
>>> city.encode('utf_8') # 'utf_?' 编码能处理任何字符串
b'S\xc3\xa3o Paulo'
>>> city.encode('utf_16')
b'\xff\xfeS\x00\xe3\x00o\x00 \x00P\x00a\x00u\x00l\x00o\x00'
>>> city.encode('iso8859_1') # 'iso8859_1' 编码也能处理字符串 'São Paulo'
b'S\xe3o Paulo'
>>> city.encode('cp437') # 'cp437' 无法编码 'ã'(带波形符的“a”)。默认的错误处理方式 'strict' 抛出 UnicodeEncodeError
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/.../lib/python3.4/encodings/cp437.py", line 12, in encode
return codecs.charmap_encode(input,errors,encoding_map)
UnicodeEncodeError: 'charmap' codec can't encode character '\xe3' in
position 1: character maps to <undefined>
>>> city.encode('cp437', errors='ignore') # error='ignore' 处理方式悄无声息地跳过无法编码的字符;这样做通常很是不妥
b'So Paulo'
>>> city.encode('cp437', errors='replace') # 编码时指定 error='replace',把无法编码的字符替换成 '?';数据损坏了,但是用户知道出了问题
b'S?o Paulo'
>>> city.encode('cp437', errors='xmlcharrefreplace') # 'xmlcharrefreplace' 把无法编码的字符替换成 XML 实体
b'São Paulo'
2.处理UnicodeDecodeError
不是每一个字节都包含有效的 ASCII 字符,也不是每一个字符序列都是有效的 UTF-8 或 UTF-16。因此,把二进制序列转换成文本时,如果假设是这两个编码中的一个,遇到无法转换的字节序列时会抛出 UnicodeDecodeError
。
另一方面,很多陈旧的 8 位编码——如 'cp1252'
、'iso8859_1'
和 'koi8_r'
——能解码任何字节序列流而不抛出错误,例如随机噪声。因此,如果程序使用错误的 8 位编码,解码过程悄无声息,而得到的是无用输出。
下面是用例子进行演示。
>>> octets = b'Montr\xe9al'
>>> octets.decode('cp1252') # 可以使用 'cp1252'(Windows 1252)解码,因为它是 latin1 的有效超集
'Montréal'
>>> octets.decode('iso8859_7') # ISO-8859-7 用于编码希腊文,因此无法正确解释 '\xe9' 字节,而且没有抛出错误
'Montrιal'
>>> octets.decode('koi8_r') # KOI8-R 用于编码俄文;这里,'\xe9' 表示西里尔字母“И”
'MontrИal'
>>> octets.decode('utf_8') # 'utf_8' 编解码器检测到 octets 不是有效的 UTF-8 字符串,抛出 UnicodeDecodeError
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xe9 in position 5:
invalid continuation byte
>>> octets.decode('utf_8', errors='replace') # 使用 'replace'
错误处理方式,\xe9
替换成了“[图片上传中。。。(1)]”(码位是 U+FFFD),这是官方指定的 REPLACEMENT CHARACTER
(替换字符),表示未知字符
'Montral'
3.使用预期之外的编码加载模块时抛出的SyntaxError
Python 3 默认使用 UTF-8 编码源码,Python 2(从 2.5 开始)则默认使用 ASCII。如果加载的 .py 模块中包含 UTF-8 之外的数据,而且没有声明编码,会得到类似下面的消息:
SyntaxError: Non-UTF-8 code starting with '\xe1' in file ola.py on line 1, but no encoding declared; see http://python.org/dev/peps/pep-0263/ for details
GNU/Linux 和 OS X 系统大都使用 UTF-8,因此打开在 Windows 系统中使用 cp1252 编码的 .py 文件时可能发生这种情况。注意,这个错误在 Windows 版 Python 中也可能会发生,因为 Python 3 为所有平台设置的默认编码都是 UTF-8。
为了修正这个问题,可以在文件顶部添加一个coding
注释
# coding: cp1252
print('Olá, Mundo!')
现在,Python 3 的源码不再限于使用 ASCII,而是默认使用优秀的 UTF-8 编码,因此要修正源码的陈旧编码(如 'cp1252')问题,最好将其转换成 UTF-8,别去麻烦 coding 注释。