【技术分享】Unicode 与 Utf-8 Utf-16等编码

1.1 背景

计算机起源于美国,英语字符二进制位之间的关系制定了一套字符编码规则,这套编码规则被称为ASCII编码。

英文字母26*2 + 各种符号,2^7就够了表示了。所以ASCII码只需要一个字节。

ASCII码

标准ASCII码 00000000 - 01111111

0-3148-5765-9097-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
10xx xxxx

2

11位

1 0000 - 1F FFFF

1110 xxxx
10xx xxxx
10xx xxxx

3

16位

20 0000 - 3FF FFFF

1111 0xxx
10xx xxxx
10xx xxxx
10xx xxxx

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,按两个字节拆分为4E59

  • 在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-8EF BB BFU+4E2D
UTF-16LEFF FEFF FE 2D 4E
UTF-16BEFE FFFE FF 4E 2D
UTF-32LEFF FE 00 00FF FE 00 00 2D 4E 00 00
UTF-32BE00 00 FE FF00 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))
"中文字符"

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值