编码与解码

编码(encode)/解码(decode)本质上是一种映射,比如‘A’用ascii编码则是65,计算机中用二进制存储的就是00110101,但计算机怎么知道00110101是‘A’呢,这就需要解码,当选用ascii解码时,计算机把00110101放到ascii码表里一查,发现是‘A’,于是就显示出’A’。

所以编码其实就是真实字符与二进制串之间的对应关系,解码就是二进制串与真实字符的对应关系

ASCII、Unicode、UTF-8的关系

ASCII

由于电脑是美国人发明的,所以最早的时候只有127个字符被编码到计算机中,即大小写英文字母,数字和一些符号,这个编码表就是ASCII编码,比如’A’的编码是65,‘a’的编码是97。

Unicode

但是要处理中文显然一个字节是不够的,至少需要两个字节,而且还不能和ASCII编码冲突,所以,中国制定了GB2312编码,用来把中文编进去。

你可以想得到的是,全世界有上百种语言,日本把日文编到Shift_JIS里,韩国把韩文编到Euc-kr里,各国有各国的标准,就会不可避免地出现冲突,结果就是,在多语言混合的文本中,显示出来会有乱码。

因此,为了把所有语言统一到一套编码里,就出现了Unicode

Unicode的标准也是不断在发展,但最常用的就是两个字节表示一个字符。现代操作系统和大多数编程语言都直接支持Unicode。

ASCII与Unicode的区别在于:ASCII用一个字节表示一个字符,Unicode用两个字节表示一个字符

字母A用ASCII编码是十进制的65,二进制的01000001;

字符0用ASCII编码是十进制的48,二进制的00110000,注意字符'0'和整数0是不同的;

汉字中已经超出了ASCII编码的范围,用Unicode编码是十进制的20013,二进制的01001110 00101101。

你可以猜测,如果把ASCII编码的A用Unicode编码,只需要在前面补0就可以,因此,A的Unicode编码是00000000 01000001。

UTF-8

新的问题又出现了:如果统一成Unicode编码,乱码问题从此消失了。但是,如果你写的文本基本上全部是英文的话,用Unicode编码比ASCII编码需要多一倍的存储空间,在存储和传输上就十分不划算。

所以,本着节约的精神,又出现了把Unicode编码转化为“可变长编码”的UTF-8编码。UTF-8就是在互联网上使用最广的一种Unicode的实现方式。

UTF-8最大的一个特点,就是它是一种变长的编码方式。它可以使用1-4个字节表示一个符号,根据不同的符号而变化字节长度。UTF-8编码把一个Unicode字符根据不同的数字大小编码成1-6个字节,常用的英文字母被编码成1个字节,汉字通常是3个字节。ASCII编码实际上可以被看成是UTF-8编码的一部分,所以,大量只支持ASCII编码的历史遗留软件可以在UTF-8编码下继续工作。

python中的编码

Python源代码文档的执行过程

我们都知道,磁盘上的文档都是以二进制格式存放的,其中文本文档都是以某种特定编码的字节形式存放的。对于进程源代码文档的字符编码是由编辑器指定的,比如我们使用VScode来编写Python进程时会指定文档编码为UTF-8,那么Python代码被保存到磁盘时就会被转换为UTF-8编码对应的字节(encode过程)后写入磁盘。

当执行Python代码文档中的代码时,Python解释器在读取Python代码文档中的字节串之后,需要将其转换为Unicode字符串(decode过程)之后才执行后续操作。

python对字符串的支持

python2

在python2中对字符串的支持有三个类:basesrting 、str 、unicode

unicode和str都是basestring的子类。严格意义上说,str其实是字节串,它是unicode经过编码后的字节组成的序列。

对’UTF-8’编码的str ‘’汉‘’ 使用len()函数时,返回的长度是3,因为‘UTF-8’编码下的‘汉‘==’xE6xB1x89’

unicode才是真正意义上的字符串,对字节串str使用正确的字符编码进行解码后获得,并且len(u’汉’)==1。

python3

python3中对字符串的支持进行了实现类层次的上简化,去掉了unicode类,添加了一个bytes类。从表面上来看,可认为Python3中的str和unicode合二为一了。

实际上,Python3中已经意识到之前的错误,开始明确区分字符串与字节。因此Python3中的str已经是真正的字符串,而字节是用单独的bytes类来表示。

也就是说,Python3默认定义的就是字符串,实现了对Unicode的内置支持,减轻了进程员对字符串处理的负担。

我们通过下面的例子来了解一下str与byte之间的转换1

2

3

4

5

6

7

8

9

10

11

12

13

14

15a = '中'

type(a)

# 显然的类型是一个'str'

>>> b = a.encode('utf-8') # 把字符串a用utf-8方式编码后赋值给b

>>>type(b)

# 于是b的类型就是'bytes'

>>> b # 这里我们把b打印出来看看

b'xe4xb8xad' # 这就是UTF-8编码下的‘中’字

>>> c = b.decode('utf-8') # 把b用utf-8方式解码后赋值给c

>>>c

'中'

>>>type(c)

简单来说,通过encode()函数将str类型的字符串转变为bytes;通过decode()将bytes类型转变为str类型。

如果bytes中有无法解码的字节,decode()就会报错:1

2

3

4>>>b'xe4xb8xadxff'.decode('utf-8')

Traceback (most recent call last):

...

UnicodeDecodeError: 'utf-8' codec can't decode byte 0xff in position 3: invalid start byte

这就要考虑是否是选择的解码方式不对

在操作字符串时,我们经常遇到str和bytes的互相转换。为了避免乱码问题,应当始终坚持使用UTF-8编码对str和bytes进行转换。