Python3中的字符串是Unicode字符串而不是字节数组。这是与Python2相比最大的差别。在Python2中,我们需要区分普通的字节为单位的字符串以及Unicode字符串。
使用UTF-8编码和解码
对字符串进行处理时,并不需要在意Python中Unicode字符的存储细节。当需要与外界进行数据交互时则需要完成两件事情:
- 将字符串__编码__为字节
- 将字节__解码__为字符串
如果Unicode包含的字符种类不超过64000种,我们就可以将字符ID统一存储在2字节中。遗憾的是,Unicode所包含的字符种类远不只此。诚然,我们将字符ID统一编码在3或4字节中,但是这样会使空间开销(内存和硬盘)增加3到4倍。两位Unix开发大神设计出了UTF-8动态编码方案。这种方案会动态的为每个Unicode字符分配1到4字节不等:
- 为ASCII字符分配1字节
- 为拉丁语系(除西里尔语)的语言分配2字节
- 为其他的位于基本多语言平面的字符分配3字节
- 为剩下的字符集分配4字节,这包括一些亚洲语言及符号
UTF-8是Python、Linux以及HTML的标准文本编码格式。
编码
__编码是将字符串转化为一系列字节的过程__。字符串的__encode()__函数所接收的第一个参数是编码方式。可选的编码方式见下表。
下面使用代码进行说明:
1 | # 定义一个Unicode字符串,它含有一个中文字符 |
程序的运行结果如下:
1 | <class 'str'> |
由上面的运行结果可以知道,单个的Unicode中文字符在UTF-8编码的格式下占用了3字节的空间。当然,你也可以使用UTF-8以外的编码方式,但该Unicode字符串可能无法被指定的编码方式处理,此时Python会抛出异常。例如将上面的Unicode中文字符编码为ascii字节,就会报错。
1 | name = '中' |
运行结果:
1 | Traceback (most recent call last): |
__encode()__函数可以接受额外的第二个参数来帮助你避免编码异常。它的默认值是’strict’,如上例所示,当函数检测到需要处理的字符串包含非ASCII字符时,会抛出UnicodeEncodeError异常。当然还有别的可选值,使用’ignore’会抛弃任何无法进行编码的字符;使用’replace’会将所有无法进行编码的字符替换为?;’backslashreplace’则会创建一个和Unicode-escape类似的Unicode字符串。
1 | name = 'ab中cd' |
程序的运行结果:
1 | ab中cd |
解码
__解码__是将字节序列转化为Unicode字符串的过程。我们从外界文本源(文件、数据库、网站、网络API等)获得的所有文本都是经过编码的字节序列。重要的是需要知道它是以何种方式编码的,这样才能逆转编码过程以获得Unicode字符串。
问题是字节序列本身不带有任何指明编码方式的信息。之前我也提到过网站随意复制粘贴文本的风险,你也可能遇到过网页乱码的情况,本应是ASCII字符的位置却被奇怪的字符占据了,这些都是编码和解码的方式不一致导致的。
1 | place = 'caf\u00e9' |
程序的运行结果:
1 | café |
上面的运行结果说明,