Python中编码解码过程(ASCII - Unicode - UTF-8)

简介

在计算机科学中,所有的信息(视频、文件、音频等)都是以二进制数字形式进行存储。而编码过程可以看成将人类的语言翻译成机器的语言,将其他类型的数据翻译成相应的数字流表达,解码过程则相反。在这个翻译的过程中就需要“密码本”,所谓的“密码本”就记录了人类语言中每个字符对应的唯一数字编号。最早出现的“密码本”叫做ASCII,后面各个国家都编码了属于自己的“密码本”,中国有GB2312编码,日本有Shift_JIS编码,韩国有Euc-kr编码。各国有各国的标准,就会不可避免地出现冲突,因此诞生了一统编码界的 Unicode “密码本”。

位(bit)和字节(byte)

在介绍编码之前,我们先来说说位(bit)和字节(byte)的定义,有利于我们理解编码过程。

  • 位(bit):音译为“比特”,表示二进制位。位是计算机内部数据储存的最小单位,11010100是一个8位二进制数。
  • 字节(byte):音译为“拜特”,习惯上用大写的“B”表示。字节是计算机中数据处理的基本单位。计算机中以字节为单位存储和解释信息,规定一个字节由八个二进制位构成,即1个字节等于8个比特(1Byte=8bit)。八位二进制数最小为00000000,最大为11111111;通常1个字节可以存入一个ASCII码,2个字节可以存放一个汉字国标码。

ASCII编码

计算机是由美国人发明的,因此他们编写了英文字符和一些控制字符与二进制之间的映射标准,即所谓的ASCII编码标准,见Python数字(number)。美国人的 ASCII 标准只定义了 128 个字符的编码方式,使用了 0000000001111111 (0-127)这个区间段的二进制。于是欧洲人使用 1000000011111111(128-255)区间段的 127 个二进制位来定义他们自己的一些符号。

Unicode编码

Unicode 的诞生统一了世界上所有的编码,它编码了世界上近乎所有的字符,总共收录将近 110 多万个字符集合,用十六进制表示,编号范围从 0x0000000x10FFFF。但大多数字符在范围:0x00000xFFFF (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()函数将十进制转换成二进制字符串。

可以看到21608对应了第三种二进制格式1110xxxx 10xxxxxx 10xxxxxx,接下来将“周”的二进制值填充进x中,注意从低位开始填充,高位不足补0,过程如下:

《Python中编码解码过程(ASCII - Unicode - UTF-8)》

这个结果对应的二进制代码为:111001011001000110101000,对应的十六进制为:0xe5 0x91 0xa8,可通过以下方式验证:

可以看出,对于“周”这个汉字,Unicode只用了两个字节,但是在UTF-8中使用了三个字节。

计算机在解码一个包含UTF-8编码的字节文件时(通常是一堆十六进制码),先将其转换成二进制数据,然后按照字节高位的值(0110111011110)逐一判断二进制格式,是截取几个字节的二进制数,然后找到对应的编号,进而通过Unicode反向推出是哪个字符。

计算机中的编码方式

在计算机内存中,统一使用Unicode编码,当需要保存到硬盘或者需要传输的时候,就转换为UTF-8编码。

《Python中编码解码过程(ASCII - Unicode - UTF-8)》

总结

编码和解码的过程,就是“人译机”和“机译人”的过程。Unicode编码统一了所有字符的编码,而UTF-8就是在Unicode编码的基础上做了改进,最常用的字符使用最少的字节数进行存储,很少用的字符使用相对多一点的字节数进行存储。

点赞