1. 小小介绍
要说utf-8,就要先大概说一下它的大哥----“unicode"。基本的ASCII定义了128个字符,从0x00-0x7f,即使后面扩展的ASCII也只能表示256个字符,即0x00-0xff。这些字符表示英文是足够了,但是对于其他语言就显得捉襟见肘了,例如咱们的汉字。于是我们就用2个字符来表示汉字了。但后来2个字符也不够了,就慢慢拓展到3个字符。美国也意思到需要一种表示全世界字符的标准,于是,unicode就这么诞生了。
unicode 规定了全球所有字符统一在一个集合,集合里面的元素互不相同。这样就为所有字符定义了一个序列,称之为码点。
unicode虽然规定了码点,但是没有规定编码方式,比如只知道“0”的码点是48(0x30),可以表示为0x00 0x30,也可以表示为0x00 0x00 0x30,只要最后的结果是码点就行。为了将所有字符正确表示,最开始使用4个字符表示unicode。确实可以表示每个字符,但是对于那些单个字符、两个字符表示的码点就太占用空间了。
这时候,变长编码存储的utf-8就出来了。
2. 闪亮登场
utf-8是一种可变长字符编码,使用1~4个字符对unicode字符集中有效码点进行编码。它规定如下:
- 对于单个字符,高位 bit7:0,兼容ASCII码;
- 对于多个字符长度n,第一个字节的bit7…bit[8-n]:1, bit[7-n]:0,其余bit进行存储序列的一部分;对于剩下的n-1个字节,bit8-bit7:10,其于bit全部用码点的剩余部分来填充;
关系表如下,x表示码点的某个bit位
码点的位数 | 码点起值 | 码点终值 | Byte 1 | Byte 2 | Byte 3 | Byte 4 | Byte 5 | Byte 6 | |
---|---|---|---|---|---|---|---|---|---|
7 | U+0000 | U+007F | 1 | 0xxxxxxx | |||||
11 | U+0080 | U+07FF | 2 | 110xxxxx | 10xxxxxx | ||||
16 | U+0800 | U+FFFF | 3 | 1110xxxx | 10xxxxxx | 10xxxxxx | |||
21 | U+10000 | U+1FFFFF | 4 | 11110xxx | 10xxxxxx | 10xxxxxx | 10xxxxxx | ||
26 | U+200000 | U+3FFFFFF | 5 | 111110xx | 10xxxxxx | 10xxxxxx | 10xxxxxx | 10xxxxxx | |
31 | U+4000000 | U+7FFFFFFF | 6 | 1111110x | 10xxxxxx | 10xxxxxx | 10xxxxxx | 10xxxxxx |
3. 敲个例子
3.1 unicode转换成utf-8
给定一个Unicode码点,可以根据二进制的bit有效位数,快速判断使用utf-8存储的有效bytes,然后套用规则即可。
Unicode code point | character | UTF-8 (hex.) | name |
---|---|---|---|
U+0400 | Ѐ | d0 80 | CYRILLIC CAPITAL LETTER IE WITH GRAVE |
如上 Ѐ
字符的码点是U+0400
,使用二进制表示0x400
,为 0100 0000 0000,得知有效bit为11,对照上表,utf-8使用2个字符进行表示,因此格式为" 0x110x xxxx 0x10xx xxxx"。从码点的二进制低位开始数数填充。参照上面的颜色 0x1101 0000 0x1000 0000,转换为16进制就是0xd0 0x80
。
python代码实现:
# unicode_str 可以改为我们实际的字符
unicode_str = "Ѐ"
# .encode 是 Python 中字符串(str)对象的一个方法,用于将字符串转换为字节对象(bytes)
utf8_bytes = unicode_str.encode('utf-8')
print(utf8_bytes)
运行之后,打印如下:
b'\xd0\x80'
3.2 utf-8转unicode
给定utf-8转换成unicode直接按照表格顺序转换就行了,例如给定0xe5 0xb5 0x8c
和0xe5 0xaf 0xbb
两段utf-8编码,我们需要分别先表示出其二进制:
UTF-8 | 二进制 |
---|---|
0xe5 0xb5 0x8c | 1110 0101 1011 0101 1000 1100 |
0xe5 0xaf 0xbb | 1110 0101 1010 1111 1011 1011 |
然后对照上面的表格,去掉每个bytes前面的固定字段后就为:0101 1101 0100 1100 和 0101 1011 1111 1011,转换为16进制就是5d4c和5bfb。
python编码如下:
# 定义一个 UTF-8 编码的字节序列
utf8_bytes = b'\xe5\xb5\x8c\xe5\xaf\xbb'
# 使用 decode 方法将 UTF-8 字节序列转换为 Unicode 字符串
unicode_str = utf8_bytes.decode('utf-8')
print(unicode_str)
for char in unicode_str:
unicode_code_point_hex = format(ord(char), 'x')
utf8_encoded_value = char.encode('utf-8')
print(f"'{char}' 的 utf-8是{utf8_encoded_value}, Unicode 码点是: {ord(char)}")
运行后,输出如下:
嵌寻
'嵌' 的 utf-8是b'\xe5\xb5\x8c', Unicode 码点是: 23884
'寻' 的 utf-8是b'\xe5\xaf\xbb', Unicode 码点是: 23547
通过以上代码,就可以根据utf-8快速找到其对应的unicode码点值了。