一、计算机的编码与解码

探讨编码与解码问题前,首先要知道什么是编码?什么是解码?

计算机最终存储在存储设备(硬盘、U盘等)上的是二进制(比如:10110101010...),每次我们打开一个文件,计算机就从硬盘将数据读入内存进行解码,然后以人类可读的方式显示给我们。当我们保存文件或者数据的时候,计算机就会把我们编辑的文字、图片和视频等全部转化为二进制保存在硬盘,这个过程叫做编码。

解码:将字节流(二进制等)转换为字符等

编码:将字符等转换为字节流(二进制等)

二、UTF-8和Unicode的区别

Unicode包含全世界所有语言的字符,刚开始的Unicode基本都是采用2字节存储,但是随着全世界语言的加入,Unicode最后基本采用四字节存储。Unicode的前128位和ASCII码一样,因此多出的字节高位全部补零(ASCII码只要一个字节存储,第一位是0,所以可以表示2^7 = 128),这样就严重的浪费了存储空间。因此,现代计算机基本上采用unicode字符的变形 : 标准的UTF-8格式。

UTF-8编码把一个Unicode字符根据不同的数字大小编码成1-6个字节,常用的英文字母被编码成1个字节,汉字通常是3个字节,只有很生僻的字符才会被编码成4-6个字节。如果你要传输的文本包含大量英文字符,用UTF-8编码就能节省空间。

需要特别注意的是:2003年11月UTF-8 被 RFC 3629 重新规范,只能使用原来Unicode定义的区域, U+0000到U+10FFFF。即最多四个字节。6字节的utf-8是很久远的存在,已经不用了。

编码方式区别:

Unicode符号范围(十六进制)

UTF-8编码方式(二进制)

0000 0000-0000007F

0xxxxxxx

0000 0080-0000 07FF

110xxxxx 10xxxxxx

0000 0800-0000 FFFF

1110xxxx 10xxxxxx 10xxxxxx

0001 0000-0010 FFFF

11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

非常具有特色的UTF-8存储方式:

1个字节第一位是0

2个字节:最高字节前三位是110.... ,其余字节前两位是10...

3个字节:最高字节前四位是1110...,其余字节前两位是10...

以此类推。

发现没,除了一个字节和ASCII码一致外,其他的都是几个字节编码,最高字节前几位就是几个1,外加一个0,其余字节的前两位都是10。规律非常简单。

实际的编码就是图中的XXX,而这些XXX和Unicode编码完全一致。就是将Unicode码按完全相同的高低顺序依次填到XXX的位置。

例如“汉”字的Unicode编码是6C49。6C49在0800-FFFF之间,所以肯定要用3字节模板了:1110xxxx 10xxxxxx 10xxxxxx。将6C49写成二进制是:0110 110001 001001, 用这个比特流依次代替模板中的x,得到:11100110 10110001 10001001,即E6 B1 89(请记住这个编码)。

三、python中的decode()和encode()函数

现在来看看python是怎么编码与解码的,在python中:

encode()函数用来编码

decode()函数用来解码

由于在python3中,所以的字符在内存中都是unicode编码,如果你想要进行编码与解码,unicode就是必须的桥梁。即,不管在硬盘等介质以什么方式存储,python内存全部将它们转化为unicode编码,然后以可视化的方式,通过输出设备(屏幕)显示给我们。

这不是我画的,来自网络

encode的作用是将unicode编码转换成其他编码的字符串,如str2.encode('utf-8'),表示将unicode编码的字符串str2转换成utf-8编码。

上图将‘汉’从uniocde字符转换为utf-8,显示的编码为b'\xe6\xb1\x89',\x后边的三个正好是e6 b1 89,关于前边的b和\x我们后边再讲。

decode的作用是将其他编码的字符串解码成unicode编码,如str1.decode('utf-8'),表示将utf-8编码的字符串str1解码成unicode编码。

上图将b'\xe6\xb1\x89'按照utf-8的方式解码成unicode

四、python3中的str和bytes对象

在python3中,str对象bytes对象完全区分开了,就是字符串对象和字节对象完全区分了。bytes对象可以按照不同的编码格式进行编码。

encode()函数的主要作用就是告诉系统,我需要将unicode按照什么格式编码成bytes对象。编码后的bytes一般输出格式前边带一个b,表示为bytes对象,\x表示为十六进制,\x后边紧跟的是编码。

如图所示例子:又是e6 b1 89

很多人很奇怪为什么英文字符就能正确显示,汉子就是\x..

汉编码为:utf-8: b'\xe6\xb1\x89'

China编码为:utf-8: b'China'

这是为什么呢?

其实本来是将‘C’、‘h’、‘i’、‘n’、‘a’按照ACSII码表中的编码写成十六进制的,即'China'转换为bytes对象为:b'\x43\x68\x69\x6e\x61',就是完全的ASCII码,只不过系统为了方便,将英文字符全部显示成原始的模样。

字符

ASCII码(十进制)

ASCII码(十六进制)

C

67

43

h

104

68

i

105

69

n

110

6e

a

97

61

decode()函数的作用就是告诉系统,我们需要将当前bytes对象按照什么编码格式进行解码。

如果编码和解码的格式不一致就会报错:

其实还可以发现python的特性,解释型语言,只有解释到错误的语句才报错,前边‘汉’的bytes对象就完整的输出了。而在C++等语言中,在编译阶段就会出错的。