简介
在计算机科学中,所有的信息(视频、文件、音频等)都是以二进制数字形式进行存储。而编码过程可以看成将人类的语言翻译成机器的语言,将其他类型的数据翻译成相应的数字流表达,解码过程则相反。在这个翻译的过程中就需要“密码本”,所谓的“密码本”就记录了人类语言中每个字符对应的唯一数字编号。最早出现的“密码本”叫做ASCII,后面各个国家都编码了属于自己的“密码本”,中国有GB2312
编码,日本有Shift_JIS
编码,韩国有Euc-kr
编码。各国有各国的标准,就会不可避免地出现冲突,因此诞生了一统编码界的 Unicode “密码本”。
位(bit)和字节(byte)
在介绍编码之前,我们先来说说位(bit)和字节(byte)的定义,有利于我们理解编码过程。
- 字节(byte):音译为“拜特”,习惯上用大写的“B”表示。字节是计算机中数据处理的基本单位。计算机中以字节为单位存储和解释信息,规定一个字节由八个二进制位构成,即1个字节等于8个比特(1Byte=8bit)。八位二进制数最小为
00000000
,最大为11111111
;通常1个字节可以存入一个ASCII码,2个字节可以存放一个汉字国标码。
ASCII编码
计算机是由美国人发明的,因此他们编写了英文字符和一些控制字符与二进制之间的映射标准,即所谓的ASCII编码标准,见Python数字(number)。美国人的 ASCII 标准只定义了 128 个字符的编码方式,使用了 00000000
— 01111111
(0-127)这个区间段的二进制。于是欧洲人使用 10000000
— 11111111
(128-255)区间段的 127 个二进制位来定义他们自己的一些符号。
Unicode编码
Unicode 的诞生统一了世界上所有的编码,它编码了世界上近乎所有的字符,总共收录将近 110 多万个字符集合,用十六进制表示,编号范围从 0x000000
到 0x10FFFF
。但大多数字符在范围:0x0000
到 0xFFFF
(0-65535)之间。Unicode编码常用两个字节表示一个字符。现代操作系统和大多数编程语言都直接支持Unicode。可以说是一本放之四海而皆准的“密码本”。因此,ASCII编码和Unicode编码的区别:ASCII编码是1个字节,而Unicode编码通常是2个字节。
字母A
用ASCII编码是十进制的65
,二进制的01000001
,Unicode编码是00000000 01000001
,可以发现就是在ASCII编码的前面加了0
补充,很好的兼容了ASCII编码。然而随之出现的问题是,如果你用Unicode编码英文的话,会比ASCII编码需要多占用一倍的存储空间。因此需要把Unicode编码转化为“可变长编码”的UTF-8
编码。UTF-8编码把一个Unicode字符根据不同的数字大小编码成1-6个字节,常用的英文字母被编码成1个字节,汉字通常是3个字节,只有很生僻的字符才会被编码成4-6个字节。
UTF-8编码
UTF-8(8-bit Unicode Transformation Format),是一种针对 Unicode 的可变长度字符编码。使用一到四个字节来编码 Unicode 字符,最常用的字符使用最少的字节数进行存储,很少用的字符使用相对多一点的字节数进行存储。
UTF-8 的编码规则如下图所示:
编号范围 | 二进制格式 |
---|---|
0x00-0x7F(0-127) | 0xxxxxxx |
0x80-0x7FF(128-2047) | 110xxxxx 10xxxxxx |
0x800-0xFFFF(2048-65535) | 1110xxxx 10xxxxxx 10xxxxxx |
0x10000-0x10FFFF(大于65535) | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx |
现在就好办了,我们只要找到任意字符在Unicode对应的编号,将这个编号代入上面的规则,使用相应的二进制格式进行处理就够了。举个例子:
汉字“周”的Unicode编号是:'0x5468'
,十进制:21608
,二进制:1010100 01101000
。可以通过ord()
函数查看十进制编号,通过hex()
函数将十进制数值转换成十六进制字符串,通过bin()
函数将十进制转换成二进制字符串。
1 2 |
>>>ord("周") 21608 |
1 2 3 4 5 |
>>>hex(21608) '0x5468' >>>bin(21608) '0b101010001101000' |
可以看到21608
对应了第三种二进制格式1110xxxx 10xxxxxx 10xxxxxx
,接下来将“周”的二进制值填充进x
中,注意从低位开始填充,高位不足补0
,过程如下:
这个结果对应的二进制代码为:111001011001000110101000
,对应的十六进制为:0xe5 0x91 0xa8
,可通过以下方式验证:
1 2 3 4 5 |
>>>hex(int('111001011001000110101000', 2)) '0xe591a8' >>>'周'.encode('utf-8') b'\xe5\x91\xa8' |
可以看出,对于“周”这个汉字,Unicode只用了两个字节,但是在UTF-8中使用了三个字节。
计算机在解码一个包含UTF-8编码的字节文件时(通常是一堆十六进制码),先将其转换成二进制数据,然后按照字节高位的值(0
、110
、1110
、11110
)逐一判断二进制格式,是截取几个字节的二进制数,然后找到对应的编号,进而通过Unicode反向推出是哪个字符。
计算机中的编码方式
在计算机内存中,统一使用Unicode编码,当需要保存到硬盘或者需要传输的时候,就转换为UTF-8编码。
总结
编码和解码的过程,就是“人译机”和“机译人”的过程。Unicode编码统一了所有字符的编码,而UTF-8就是在Unicode编码的基础上做了改进,最常用的字符使用最少的字节数进行存储,很少用的字符使用相对多一点的字节数进行存储。