概览
"string"的概念很简单,就是字符的序列。但是怎么定义字符是一个问题。在python3,str是由Unicode字符组成,而在python2里str是由原始的byte字符组成。
Unicode标准把字符的标识和字符的二进制表示区分开来:
- 字符的标识,即码位(code point),是一个从0到1,114,111的数字,在Unicode标准里是以U+为前缀的4到6位的16进制数字。如字母A的码位是U+0041。
- 实际的一个字符的二进制表示是由具体使用的编码(encoding)决定。如字母A在UTF-8编码里是单个字节\x41而在UTF-16LE编码下是两个字节\x41\x00。
从码位转换字节是encoding,从字节转换到码位是decoding。
字符编码的各式各样的问题
本章后面讲了很多因为字符编码产生的问题,在目前做过的项目中基本没碰到过,感觉主要出现在像希腊语,葡萄牙语还有泰米尔语这类语言上,也没去深入研究,简单做下总结。
- 有些编码包含的字符只是Unicode的一个子集,当把一个Unicode字符转换到目标编码时,如果目标编码不包含这个字符的定义,则会报UnicodeEncodeError,可以通过在
encode
方法中加入error参数,对错误进行忽略或者做一些处理。 - 如果二进制编码有错,不是一个有效的编码,则在把二进制编码转换到Unicode字符时,会报UnicodeDecodeError,也可以通过在decode方法中加入error参数,对错误进行忽略或者做一些处理。
- 从python3开始,源码的默认编码采用UTF-8,而python2的默认编码是ASCII,意味着
#!coding: utf8
不需要再加了。 - 作者讨论了下是不是能从二进制序列里发现编码方式,答案是不可以。但是可以通过一些特殊的规则大概确定一下,作者介绍了个chardet的项目。
- BOM是用来标识大端序和小端序的,对于UTF-16是需要的,但是对于UTF-8不是必须的,python没有使用BOM来判断一个文件是否是UTF-8。
- 如果使用python默认的编码,可能会出问题,比如用UTF-8编码写一个文件,但是读的时候没指定编码,在Linux上没问题,但是可能在Windows上就有问题。最好是在所有的操作上都明确指定编码格式。
- 因为组合字符的原因,字符串比较会有些问题,打印出来是一样的字符,可能实际的编码不一样,python提供了一些normalize方法进行归一化,不过作者也提到像google就是直接把这些变音符给去掉。
- 在字符串排序时有可能也会出现问题,作者推荐了个PyUCA。
- python中有一个完整的Unicode database,记录了每个字符的各方面的信息。
- python的接口可以支持str和bytes的输入参数,但是根据参数类型不同,结果也不同。
- python提供了struct模块去对打包在一起的bytes进行拆包。
>>> import struct
>>> fmt = '<3s3sHH' #
>>> with open('filter.gif', 'rb') as fp: ... img = memoryview(fp.read()) # ...
>>> header = img[:10] #
>>> bytes(header) # b'GIF89a+\x02\xe6\x00'
>>> struct.unpack(fmt, header) # (b'GIF', b'89a', 555, 230)
>>> del header #
>>> del img