0%

Python学习笔记(Python3编码)

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 定义一个Unicode字符串,它含有一个中文字符
name = "中"
# 打印字符串的类型
print(type(name))
# 打印字符串
print(name)
# 打印字符串的长度,最后结果为1,说明len()获取的是字符串中字符的个数,不是字节长度
print(len(name))

# 将字符串编码为UTF-8的字节序列
ds = name.encode('utf-8')
print("--------")

# 打印字节序列的类型
print(type(ds))
# 打印字符串编码过后的字节序列
print(ds)
# 打印字节序列的长度,发现一个中文字符在UTF-8编码中占3个字节
print(len(ds))

程序的运行结果如下:

1
2
3
4
5
6
7
<class 'str'>

1
--------
<class 'bytes'>
b'\xe4\xb8\xad'
3

由上面的运行结果可以知道,单个的Unicode中文字符在UTF-8编码的格式下占用了3字节的空间。当然,你也可以使用UTF-8以外的编码方式,但该Unicode字符串可能无法被指定的编码方式处理,此时Python会抛出异常。例如将上面的Unicode中文字符编码为ascii字节,就会报错。

1
2
3
name = '中'
ds = name.encode('ascii')
print(ds)

运行结果:

1
2
3
4
Traceback (most recent call last):
File "/Users/kris/PycharmProjects/pythons_demo/Python语言及其应用/第七章/test.py", line 26, in <module>
ds = name.encode('ascii')
UnicodeEncodeError: 'ascii' codec can't encode character '\u4e2d' in position 0: ordinal not in range(128)

__encode()__函数可以接受额外的第二个参数来帮助你避免编码异常。它的默认值是’strict’,如上例所示,当函数检测到需要处理的字符串包含非ASCII字符时,会抛出UnicodeEncodeError异常。当然还有别的可选值,使用’ignore’会抛弃任何无法进行编码的字符;使用’replace’会将所有无法进行编码的字符替换为?;’backslashreplace’则会创建一个和Unicode-escape类似的Unicode字符串。

1
2
3
4
5
name = 'ab中cd'
print(name)
print(name.encode('ascii', 'ignore'))
print(name.encode('ascii', 'replace'))
print(name.encode('ascii', 'backslashreplace'))

程序的运行结果:

1
2
3
4
ab中cd
b'abcd'
b'ab?cd'
b'ab\\u4e2dcd'

解码

__解码__是将字节序列转化为Unicode字符串的过程。我们从外界文本源(文件、数据库、网站、网络API等)获得的所有文本都是经过编码的字节序列。重要的是需要知道它是以何种方式编码的,这样才能逆转编码过程以获得Unicode字符串。

问题是字节序列本身不带有任何指明编码方式的信息。之前我也提到过网站随意复制粘贴文本的风险,你也可能遇到过网页乱码的情况,本应是ASCII字符的位置却被奇怪的字符占据了,这些都是编码和解码的方式不一致导致的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
place = 'caf\u00e9'
print(place)
print(type(place))

# 编码
place_tytes = place.encode('utf-8')
print(place_tytes)
print(type(place_tytes))

# 解码
place2 = place_tytes.decode('utf-8')
print(place2)

# 使用ASCII解码,会报错
# place3 = place_tytes.decode('ascii')
# print(place3)

# 使用其他编码格式解码
place4 = place_tytes.decode('latin_1')
print(place4)

place5 = place_tytes.decode('windows-1252')
print(place5)

程序的运行结果:

1
2
3
4
5
6
7
café
<class 'str'>
b'caf\xc3\xa9'
<class 'bytes'>
café
café
café

上面的运行结果说明,