4.1 字符问题
Unicode标准把字符的标识和具体的字节表述进行如下区分:
- 字符的标识,即码位,是0~1114111的数字(十进制),在Unicode标准中以4—6个十六进制数标识,而且前缀加“U+”。如字母A的码位是U+0041。
- 字符的具体表述取决于所用的编码。编码是在码位和字节序列之间转换时使用的算法。在UTF-8编码中,A(U+0041)的码位编码成单个字节\x41
将码位转换成字节序列的过程是编码,把字节序列转换成码位的过程是解码。如下例:
>>> b="哈哈哈".encode('utf-8')
>>> b
b'\xe5\x93\x88\xe5\x93\x88\xe5\x93\x88'
>>> b.decode('utf-8')
'哈哈哈'
4.2 字节概要
Python内置两种基本二进制序列类型:Python3引入的不可变bytes类型和Python2.6添加的可变bytearray类型。
二者对象的各个元素是介于0-255之间的整数。二进制序列的切片始终是同一类型的二进制序列,包括长度为1的切片。如下所示:
>>> a=bytes('哈哈哈',encoding='utf-8')
>>> a
b'\xe5\x93\x88\xe5\x93\x88\xe5\x93\x88'
>>> a[0]
229
>>> a[:1]
b'\xe5'
>>> a_arr=bytearray(a)
>>> a_arr
bytearray(b'\xe5\x93\x88\xe5\x93\x88\xe5\x93\x88')
>>> a_arr[-1:]
bytearray(b'\x88')
a[0]返回229,a[:1]返回bytes对象切片。
虽然二进制序列其实是整数序列,但是它们的字面量表示法表明其中有ASCII文本。因此,各个字节的值可能会使用如下三种不同的显示方式:
- 可打印的ASCII范围内的字节(从空格到~),使用ASCII字符本身
- 制表符、换行符、回车符和\对应的字节,使用转义序列\t、\n、\r和\
- 其他字节的值,使用十六进制转义序列
结构体和内存视图
struct模块提供了一些函数,可把打包的字节序列转换成不同类型字段组成的元组,还有一些函数用于执行反向转换,把元组转换成打包的字节序列。
struct模块能处理bytes、bytearray和memoryview对象。
4.3 基本的编解码器
*表明该编码不支持表示该字符
典型编码介绍如下:
- latin1:一种重要的编码,是其他编码的基础
- cp1252:Microsoft指定的lantin1超集
- cp437:IBM PC最初的字符集,包含框图符号
- gb2312:用于编码简体中文的陈旧标准
- utf-8:目前Web中最常见的8位编码,与ASCII兼容
- utf-16le:UTF-16的16位编码方案的一种形式
4.4 了解编解码问题
4.4.1 处理UnicodeEncodeError
多数非UTF编解码器只能处理Unicode字符的一小部分子集。把文本转换成字节序列时,如果目标编码中没有定义某个字符,那就会抛出UnicodeEncodeError异常,除非把errors参数传递给编码方法或函数,对错误进行特殊处理。如下例所示:
>>> city="北京"
>>> city.encode('utf-8')
b'\xe5\x8c\x97\xe4\xba\xac'
>>> city.encode('gb2312')
b'\xb1\xb1\xbe\xa9'
>>> city.encode('cp437')
Traceback (most recent call last):
File "<pyshell#20>", line 1, in <module>
city.encode('cp437')
File "G:\Python\lib\encodings\cp437.py", line 12, in encode
return codecs.charmap_encode(input,errors,encoding_map)
UnicodeEncodeError: 'charmap' codec can't encode characters in position 0-1: character maps to <undefined>
>>> city.encode('cp437',errors="ignore")
b''
>>> city.encode('cp437',errors='replace')
b'??'
>>> city.encode('cp437',errors="xmlcharrefreplace")
b'北京'
当cp437无法编码汉字时,抛出UnicodeEncodeError。
对error参数的设置说明如下:
- ignore:悄无声息地跳过无法编码的字符串
- replace:将无法编码的字符替换成‘?’
- xmlcharrefreplace:把无法编码的字符替换成XML实体
4.4.2 处理UnicodeDecodeError
不是每一个字节都包含有效的ASCII字符,也不是每一个字符序列都是有效的UTF-8或UTF-16。因此,把二进制序列转换成文本时,如果假设是这两个编码中的一个,遇到无法转换的字节序列时会抛出UnicodeDecodeError。如下例:
>>> octets=b'Montr\xe9al'
>>> octets.decode('cp1252')
'Montréal'
>>> octets.decode('iso8859_7')
'Montrιal'
>>> octets.decode('koi8_r')
'MontrИal'
>>> octets.decode('utf-8')
Traceback (most recent call last):
File "<pyshell#4>", line 1, in <module>
octets.decode('utf-8')
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xe9 in position 5: invalid continuation byte
>>> octets.decode('utf-8',errors='replace')
'Montr�al'
不同的编解码器将相同的字节解码成不同的字符,对于utf-8解码器,抛出异常。
4.4.3 使用预期之外的编码加载模块时抛出的SyntaxError
Python3默认使用UTF-8编码源码。GNU/Linux和OS X系统大都使用UTF-8编码,因此打开Windows系统中使用cp1252编码的.py文件时可能发生报错。
为修正此问题,可在文件顶部添加一个神奇的coding注释。
# coding: utf-8
python允许在源码中使用非ASCII标识符,这意味着变量名,函数名等可以是中文。