目录
2.3 – Python 字符串的 encode 和 decode 方法
一、ASCII 字符集和编码
计算机只能存储二进制数字 0 和 1,因此无论是文本中的数字、字母、汉字以及 emoji 都需要以某种方式转换成二进制数字进行存储,需要的时候再读出来。
1963 年,ANSI 推出了 ASCII 作为计算机及其他设备的文本字符编码标准
。ASCII 支持的字符包含 0 ~ 9 的阿拉伯数字、大小写英文字母、常用的英文符号(!”#$%&'()*+,-./:;<=>?@[\]^_`{|}~)以及控制字符(负责对应换行、回车等特殊的控制功能),
每个字符都有一个对应的数字,叫作码点,ASCII 的码点为 0 ~ 127 之间的数字,标准所支持的所有字符以及对应码点的集合叫作字符集
。
ANSI 的全称是 American National Standards Institute,即美国国家标准学会。
ASCII 的全称是 American Standard Code for Information Interchange,即美国信息交换标准代码。
ASCII 字符集(一共包含 128 个字符):
计算机可以存储 ASCII 码点的二进制,但由于最大的码点是 127,即二进制的长度小于或等于 7,并且计算机一般以 8 比特,即 1 字节,为基本单位读写,所以为了凑整,这些二进制被存储时,会在开头留 0,用固定的 8 比特长度来存储每个字符。
字符 | 码点 | 二进制码点 | 存储在计算机的内容 |
---|---|---|---|
NUL | 0 | 0 | 00000000 |
SOH | 1 | 1 | 00000001 |
… … | … … | … … | … … |
0 | 48 | 110000 | 00110000 |
1 | 49 | 110001 | 00110000 |
… … | … … | … … | … … |
A | 65 | 1000001 | 01000001 |
B | 66 | 1000010 | 01000010 |
… … | … … | … … | … … |
~ | 126 | 1111110 | 01111110 |
DEL | 127 | 1111111 | 01111110 |
这种从字符到计算机能够存储的内容之间的映射叫作编码
。
二、Unicode 字符集
ASCII 编码一共就适用于 128 个字符,其他语言的字符不够用,于是不同国家和地区开始制定自己的编码标准,比如大陆有国家总局制定的于 1981 年开始实施的
GB2312
,港澳台有在 1984 年开始流行的大五码
Big5
,后来还有对 GB2312 进行扩展的
GBK
,收录了简繁体汉字、日文、韩文等。
但是当标准不统一时,乱码问题也随之产生,因为计算机内存里的同一个数字在不同字符集中代表的可能是完全不同的字符
。
因此大家亟需一种更通用的字符编码,支持不同语言的文字,
1991 年 Unicode(统一码、万国码、单一码)字符集发布,目标在于让世界上每个人都能在电脑上阅读自己的文字
。随着版本的迭代,Unicode 囊括的字符越来越多,包括汉字、平片假名、藏文、阿拉伯文,甚至古象形文字,2010 年,emoji 也被纳入 Unicode,如今 Unicode 已经包含了超过十四万个字符。Unicode 的每个字符也有对应码点,许多语言都有可以用于查询 Unicode 字符码点的内置函数,例如在 Python 中,可以使用
内置函数
ord
获取字符对应的码点(还可以使用
内置函数
chr
根据码点获取对应的字符)。
print(ord('A')) # 65
print(ord('你')) # 20320
print(chr(65)) # A
print(chr(20320)) # 你
2.1 – UTF-32 编码
注意:字符集只是字符以及字符对应码点的集合,不代表字符一定以对应码点被存储在计算机中,字符编码才是真正定义了字符到计算机存储内容的映射
。
那么最简单的编码规则自然就是把字符对应的码点直接以二进制存储在计算机中,
UTF-32(32-bit Unicode Transformation Format) 编码就是针对 Unicode 这样做的
,上面所说的 ASCII 编码也是针对 ASCII 字符集这样做的。
UTF-32 让每个字符都以 32 比特,即 4 字节的长度来存储,位数不够就在前面补 0,32 比特足够表示 Unicode 中的所有字符,固定长度也能帮助计算机明确每个字符的截断范围
,但造成的问题是:ASCII 编码中的每个英文字母只要 1 字节,GBK 中的一个汉字只要 2 字节,而在 UTF-32 中则都需要 4 字节。
2.2 – UTF-8 编码
为了改善空间效率,UTF-8(8-bit Unicode Transformation Format)在 1992 年诞生,UTF-8 是针对 Unicode 的可变长度编码,不同于编码后长度固定为 32 比特的 UTF-32,UTF-8 针对不同字符,编码后的长度可以是 32 比特、24 比特、16 比特、8 比特
。
具体规则是
:
-
码点在
0 ~ 127
范围内的字符直接映射为 1 字节长度的二进制数。 -
码点在
128 ~ 2047
范围内的字符映射为 2 字节长度的二进制数。
UTF-8 为了让计算机知道各个字符之间到底在哪里分割,就让 2 字节编码的第一个字节由 110 开头,第二个字节由 10 开头,Unicode 的二进制码点会被分割成两个部分,填入 UTF-8 编码的数字里
。 -
码点在
2048 ~ 65535
范围内的字符映射为 3 字节长度的二进制数。
第一个字节由 1110 开头,后面两个字节都由 10 开头,Unicode 的二进制码点会被分割成三个部分,填入 UTF-8 编码的数字中
。 -
码点在
65536 ~ 1114111
范围的字符映射为 4 字节长度的二进制数。
第一个字节由 11110 开头,后面三个字节都由 10 开头,Unicode 的二进制码点会被分割成四个部分,填入 UTF-8 编码的数字中
。
码点范围(十进制) | 二进制码点 | 存储在计算机的内容 |
---|---|---|
0 ~ 127 |
0 zzzzzzz |
0 zzzzzzz |
128 ~ 2047 |
00000 yyy yy zzzzzz |
110 yyyyy 10 zzzzzz |
2048 ~ 65535 |
xxxx yyyy yy zzzzzz |
1110 xxxx 10 yyyyyy 10 zzzzzz |
65536 ~ 1114111 |
000 www xx xxxx yyyy yy zzzzzz |
11110 www 10 xxxxxx 10 yyyyyy 10 zzzzzz |
此处 w、x、y、z 占位符均表示 0 或 1
。
UTF-8 的优点
:
-
兼容 ASCII。
-
节约空间,UTF-8 让 Unicode 中码点小的字符也相应拥有更短的长度,并且通过前缀信息,也能让计算机辨别各字符在内存中的总长度,解决分割不明的问题。
2.3 – Python 字符串的 encode 和 decode 方法
使用 encode 方法可以将字符串以指定的编码规则转换成对应的编码(注意:不是码点);使用 decode 方法可以将编码按照指定的编码规则解码成字符串
。
# 字符:你
# 码点范围:20320(2048 ~ 65535)
# 二进制码点:01001111 01100000
# 存储在计算机中的内容:11100100 10111101 10100000
# 验证:
print('你'.encode('utf-8')) # b'\xe4\xbd\xa0' --> 0xe4bda0
print(bin(0xe4bda0)) # 0b11100100 10111101 10100000
print(b'\xe4\xbd\xa0'.decode('utf-8')) # 你