1.1 背景
计算机起源于美国,英语字符与二进制位之间的关系制定了一套字符编码规则,这套编码规则被称为ASCII编码。
英文字母26*2 + 各种符号,2^7就够了表示了。所以ASCII码只需要一个字节。
ASCII码
标准ASCII码 00000000
- 01111111
0-31 | 48-57 | 65-90 | 97-122 | 其他 |
---|---|---|---|---|
控制或通信专用字符 | 数字 [0-9] | 大写字母 [A-Z] | 小写字母 [a-z] | 特殊符号 [!@#$%.]等 |
控制字符:LF(换行)、CR(回车)、FF(换页)、DEL(删除)、BS(退格)、BEL(响铃)等
通信专用字符:SOH(文头)、EOT(文尾)、ACK(确认)等
1.2 EASCII(Extended ASCII)
当ASCII码到欧洲的时候,128个不够用了,欧洲国家就决定对ASCII编码进行适当的“改造”。
把闲置的最高位用起来, 就有2^8 256种编码。
多语种
欧洲语种特别多,256个也不够用了。
在EASCII中表示的256个字符中,0-127
=ASCII编码,128-255
=国家地区自定义编码
- ISO8859-1 字符集,也就是 Latin-1,是西欧常用字符,包括德法两国的字母。
- ISO8859-2 字符集,也称为 Latin-2,收集了东欧字符。
- ISO8859-3 字符集,也称为 Latin-3,收集了南欧字符。
1.3 其他编码
计算机传到了亚洲,256个字符怎么也不够用了,比如光中国的常用汉字就有3000多个,在不同的地区和国家又出现了很多字符编码
- 大陆的 GB2312
- 港台的 BIG5
- 日本的 Shift JIS
- 韩语、阿拉伯语等等
问题
同一个二进制数据,不同的编码会解析出不同的字符
不同国家之间的交流经常会出现乱码
人们迫切的希望有一种统一的规则, 对所有国家和地区的字符进行编码
1.4 Unicode
官方中文名称为统一码,也译名为万国码、国际码、单一码
Unicode 是国际标准字符集,任何文字在Unicode中都对应一个值,以满足跨语言、跨平台的文本信息转换
这个值称为代码点(code point)。代码点的值通常写成 U+ABCD 的格式。
比如:
汉字 "中" 的 码点是 0x4E2D
,
大写字母 A 的码点是 0x41
,
具体字符对应的 Unicode 编码可以查询 Unicode字符编码表
2.1 UCS(Universal Coded Character Set)通用字符集
格式 | 长度 | 范围 | 码位 | 备注 |
---|---|---|---|---|
UCS-2 | 2字节 | U+0000 ~ U+FFFF | 2^16 | |
UCS-4 | 4字节 | U+00000000 ~ U+7FFFFFFF | 2^31 | 4字节【group】【plane】【roll】【cell】 |
问题
如何才能区别Unicode和ASCII?
计算机怎么知道三个字节表示一个符号,而不是分别表示三个符号呢?
英文字母只用一个字节表示就够了,如果unicode统一规定,每个符号用三个或四个字节表示,那么每个英文字母前都必然有二到三个字节是0,这对于存储来说是极大的浪费
2.2 UTF (Unicode Transformation Format)
Unicode 出现了多种存储方式,常见的有 UTF-8、UTF-16、UTF-32,它们分别用不同的二进制格式来表示 Unicode 字符
2.2.1 UTF-8 编码
UTF-8: 是一种变长字符编码,被定义为将码点编码为 1 至 4 个字节,具体取决于码点数值中有效二进制位的数量
UTF-8 的编码规则:
- 对于单字节的符号,字节的第一位设为 0,后面 7 位为这个符号的 Unicode 码。因此对于英语字母,UTF-8 编码和 ASCII 码是相同的, 所以 UTF-8 能兼容 ASCII 编码,这也是互联网普遍采用 UTF-8 的原因之一
- 对于 n 字节的符号( n > 1),第一个字节的前 n 位都设为 1,第 n + 1 位设为 0,后面字节的前两位一律设为 10 。剩下的没有提及的二进制位,全部为这个符号的 Unicode 码
Unicode编码范围(16进制) | UTF-8编码方式(二进制) | 字节数 | 有效码位 |
---|---|---|---|
0000 - 007F | 0xxx xxxx ASCII码 | 1 | 7位 |
0080 - 07FF | 110x xxxx | 2 | 11位 |
1 0000 - 1F FFFF | 1110 xxxx | 3 | 16位 |
20 0000 - 3FF FFFF | 1111 0xxx | 4 | 21位 |
... | ... | 5/6 | 26位/31位 |
例如:汉字 "中" 的 码点是 0x4E2D
对应二进制数据为 0100 1110 0010 1101, 16位
utf-8 编码后为3个字节
11100100 10111000 10101101
2.2.2 UTF-16 编码
UTF-16由RFC2781规定,它使用两个字节来表示一个代码点.UTF-16是完全对应于UCS-2的.
2.2.3 UTF-32 编码
UTF-32 是固定长度的编码,始终占用 4 个字节,足以容纳所有的 Unicode 字符,所以直接存储 Unicode 码即可,不需要任何编码转换。虽然浪费了空间,但提高了效率。
问题
如某字符为十六进制编码4E59
,按两个字节拆分为4E
和59
- 在Mac上从低字节开始读取,编码为
U+594E
,找到的字符为“奎”
, - 在Windows上从高字节开始读取,编码为
U+4E59
,字符为“乙”
。于是在UTF-16编码实现方式中使用了大端序(Big-Endian,简写为UTF-16 BE)、小端序(Little-Endian,简写为UTF-16 LE)的概念,以及可附加的字节顺序记号解决方案.
2.2.4 BOM (byte-order mark)
是 "字节序标记" 的意思, 它常被用来当做标识文件是以 UTF-8、UTF-16 或 UTF-32 编码的标记
编码 | 固定文件头 | 数据示例(“中”) |
---|---|---|
UTF-8 | EF BB BF | U+4E2D |
UTF-16LE | FF FE | FF FE 2D 4E |
UTF-16BE | FE FF | FE FF 4E 2D |
UTF-32LE | FF FE 00 00 | FF FE 00 00 2D 4E 00 00 |
UTF-32BE | 00 00 FE FF | 00 00 FE FF 00 00 4E 2D |
3.常见问题
1.Redis中文key
test.default.redis:6379> set tt 中国
-> Redirected to slot [14990] located at 10.20.12.106:6379
OK
(0.55s)
10.20.12.106:6379> get tt
"\xd6\xd0\xc4\xcf"
10.20.12.106:6379> exit
连接时添加 --raw
❯ redis-cli -h test.default.redis -c --raw
test.default.redis:6379> set tt 中国
-> Redirected to slot [14990] located at 10.20.12.106:6379
OK
10.20.12.106:6379> get tt
中国
2. json dumps 中文
指定了ensure_ascii=False使序列化后的数据不强制使用ascii吗转码, 输出即是中文
In [1]: import json
In [2]: print(json.dumps("中文字符"))
"\u4e2d\u6587\u5b57\u7b26"
In [3]: print(json.dumps("中文字符", ensure_ascii=False))
"中文字符"