在自然语言处理中(NLP),文本是模型的输入,但计算机并不能直接理解“字”,而是通过数字和二进制来存储和处理字符。这篇文章将以中文 "你"为例,详细讲解从 Unicode 码点 → UTF-8 字节序列 的全过程,并配合 Python 代码逐步验证。
本文将介绍以下内容:
- 1. Unicode 与 UTF-8 的背景
- 2. 示例:汉字 “你” 的编码过程
- 3. UTF-8 三字节协议的设计原因
- 4. 总结整体代码x
- 5. 如何快速确认文本需要几个字节表示
- 6. 总结
一、Unicode 与 UTF-8 的背景
1. Unicode:通用字符集
Unicode 是一种 字符集,旨在涵盖地球上几乎所有的书写系统和字符。它为每个字符分配了一个唯一的 代码点(code point) 来标识字符。
- 例如
"你"的 Unicode 码点是U+4F60。 - Unicode 不关心字符在计算机内部的存储方式,它只是定义了字符与号码的对应关系。
Unicode 的出现,解决了过去不同语言使用不同编码导致的“乱码”和“碎片化”问题,让所有字符都能在一个统一的标准下共存。
2. UTF-8:Unicode 的存储方式
Unicode 只提供“码点”,但实际存储时需要具体的 编码方案。其中使用最广泛的就是 UTF-8。
UTF-8(Unicode Transformation Format - 8)是一种 变长字符编码:
- 兼容 ASCII:所有 ASCII 字符(U+0000 – U+007F)直接用 1 个字节表示,不会有任何变化。
- 多字节编码:更大的 Unicode 码点会使用 2~4 个字节存储。
- 每个字节的前缀位(高位)用于指示字符的长度与字节角色。
规则如下:
- 单字节符(0x00–0x7F):
0xxxxxxx - 双字节符(0x80–0x07FF):
110xxxxx 10xxxxxx - 三字节符(0x0800–0xFFFF):
1110xxxx 10xxxxxx 10xxxxxx - 四字节符(0x10000–0x10FFFF):
11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
因此 UTF-8 可以覆盖整个 Unicode 字符集,并且保证 ASCII 文件和 UTF-8 文件完全兼容。
二、示例:汉字 "你" 的编码过程
我们现在用
"你"来演示 Unicode 与 UTF-8 转换的全过程。
步骤 1:获取 Unicode 码点
char = "你"
code_point = ord(char) # 获取 Unicode 码点
print(hex(code_point), code_point)
输出:
0x4f60 20320
说明 "你" 的 Unicode 码点是 U+4F60(十进制 20320)。
步骤 2:转成二进制
binary_repr = bin(code_point)[2:].zfill(16) # 转成16位二进制
print(binary_repr)
输出:
0100111101100000
步骤 3:确定 UTF-8 模板
根据 UTF-8 的规则:
U+4F60位于0x0800–0xFFFF- 所以
"你"需要用 3 个字节表示,模板是:
1110xxxx 10xxxxxx 10xxxxxx
步骤 4:填充二进制位
把 0100 1111 0110 0000(16 位)依次填入模板中的 x:
bits = binary_repr
byte1 = "1110" + bits[:4] # 高4位
byte2 = "10" + bits[4:10] # 中间6位
byte3 = "10" + bits[10:] # 低6位
print(byte1, byte2, byte3)
输出:
11100100 10111101 10100000
步骤 5:转为字节序列
bytes_list = [int(byte1, 2), int(byte2, 2), int(byte3, 2)]
print(bytes_list)
print(bytes(bytearray(bytes_list)))
输出:
[228, 189, 160]
b'\xe4\xbd\xa0'
步骤 6:直接用 Python 内置方法验证
print(list("你".encode("utf-8")))
输出:
[228, 189, 160]
验证无误。
三、为什么是 1110xxxx 10xxxxxx 10xxxxxx
UTF-8 的设计有其深层原因:
-
兼容 ASCII
- ASCII 范围内的字符保持
0xxxxxxx格式,完全不变。 - 保证老系统、英文文档无需修改。
- ASCII 范围内的字符保持
-
前缀位区分长度
0,110,1110,11110分别标识 1~4 字节长度。- 解码时只需读首字节就能确定后续字节数。
-
后续字节以
10开头- 避免二义性:解码器一眼就能区分“新字符的起始字节”和“延续字节”。
- 保证解码唯一、无歧义。
-
存储效率与通用性平衡
- 英文等常用字符仍是 1 字节。
- 中文、日文、韩文等常用字符 3 字节。
- Emoji 等稀有字符最多 4 字节。
- 做到了效率与兼容的兼顾。
四、整体代码(封装版)
def char_to_utf8_bytes(ch):
code_point = ord(ch)
print(f"Unicode 码点: U+{code_point:X} ({code_point})")
bits = bin(code_point)[2:].zfill(16)
print(f"二进制: {bits}")
# UTF-8 三字节模板填充
byte1 = "1110" + bits[:4]
byte2 = "10" + bits[4:10]
byte3 = "10" + bits[10:]
utf8_bytes = [int(byte1, 2), int(byte2, 2), int(byte3, 2)]
return utf8_bytes
print(char_to_utf8_bytes("你"))
print(list("你".encode("utf-8")))
输出:
Unicode 码点: U+4F60 (20320)
二进制: 0100111101100000
[228, 189, 160]
[228, 189, 160]
五、如何快速确认文本需要几个字节
1. UTF-8 的编码范围划分如下:
| Unicode 范围 | 字节数 | 模板格式 |
|---|---|---|
| 0x0000–0x007F | 1 | 0xxxxxxx |
| 0x0080–0x07FF | 2 | 110xxxxx 10xxxxxx |
| 0x0800–0xFFFF | 3 | 1110xxxx 10xxxxxx 10xxxxxx |
| 0x10000–0x10FFFF | 4 | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx |
2. Python 实现:
def utf8_template(ch):
code_point = ord(ch)
if code_point <= 0x7F:
return "1 字节: 0xxxxxxx"
elif code_point <= 0x7FF:
return "2 字节: 110xxxxx 10xxxxxx"
elif code_point <= 0xFFFF:
return "3 字节: 1110xxxx 10xxxxxx 10xxxxxx"
elif code_point <= 0x10FFFF:
return "4 字节: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx"
else:
return "超出 Unicode 范围"
print("你:", utf8_template("你"))
print("A:", utf8_template("A"))
print("€:", utf8_template("€")) # 欧元符号 U+20AC
print("😀:", utf8_template("😀")) # emoji U+1F600
运行结果
你: 3 字节: 1110xxxx 10xxxxxx 10xxxxxx
A: 1 字节: 0xxxxxxx
€: 3 字节: 1110xxxx 10xxxxxx 10xxxxxx
😀: 4 字节: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
六、总结
- Unicode 是字符集,给每个字符分配唯一的代码点。
- UTF-8 是编码方案,把 Unicode 码点转成字节序列。
"你"的 Unicode 是U+4F60,UTF-8 编码后是[228, 189, 160](b'\xe4\xbd\xa0')。- UTF-8 的设计保证了 ASCII 兼容、解码唯一性,以及在效率和通用性上的平衡。
2万+

被折叠的 条评论
为什么被折叠?



