文本在计算机中是一串二进制。本文整理从 ASCII 到 Unicode、再到 UTF-8/UTF-16/UTF-32 的编码演进,重点说明 UTF-8 的变长原理与"码点 vs 编码单元"的区别。
要让计算机处理文本,需为每个字符分配一个唯一编号(码点,Code Point),再用二进制存储。这个过程叫编码(Encoding)。字符集与具体存储编码是两件事,需区分清楚。
A=65=0x41、a=97=0x61、0=48=0x30。局限:仅覆盖英文字符,无法表示中文、日文、阿拉伯文等。
各国各自定义扩展编码:
问题:同一字节序列在不同编码下含义不同,跨系统传输易产生乱码(mojibake)。这正是需要统一标准的原因。
Unicode 为全世界所有字符分配唯一码点,用 U+XXXX 表示,如 中 = U+4E2D。目前码点范围约 0 ~ 0x10FFFF(约 111 万),分为 17 个平面,日常字符多在基本多文种平面(BMP,U+0000 ~ U+FFFF)。
关键:Unicode 只定义"这个码点对应什么字符",不规定如何存到字节。存储方案由 UTF(Unicode Transformation Format) 决定。
每个码点固定 4 字节。实现最简单、索引最快,但空间浪费大(英文也占 4 字节),几乎不用于存储。
用 1~4 字节表示不同范围的码点,兼容 ASCII(0x00-0x7F 与 ASCII 完全一致)。
| Unicode 范围 | UTF-8 字节数 | 编码格式 |
|---|---|---|
| U+0000 ~ U+007F | 1 | 0xxxxxxx |
| U+0080 ~ U+07FF | 2 | 110xxxxx 10xxxxxx |
| U+0800 ~ U+FFFF | 3 | 1110xxxx 10xxxxxx 10xxxxxx |
| U+10000 ~ U+10FFFF | 4 | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx |
特点:
中=U+4E2D)占 3 字节:11100100 10111000 10101101(即 E4 B8 AD)。中 = U+4E2D = 二进制 100111000101101(15 位)。1110 + 高 4 位 0100 = 1110010010 + 中 6 位 111000 = 1011100010 + 低 6 位 101101 = 10101101E4 B8 AD,与 中 的 UTF-8 完全一致。工程陷阱:很多语言 length() 返回的是"编码单元数"而非"字符数"。例如一个 emoji(U+1F600,代理对)在 UTF-16 下占 2 个编码单元,"😀".length 可能返回 2。处理时需注意。
length 与实际字符数不符的陷阱。