1. 简介
Unicode 是一种用于程序中字符表示的标准,它支持庞大的字符集。截至 Unicode 15.0,它包含了 149,186 个不同的字符。为了能够存储这么多字符,我们必须使用合适的编码方式来表示它们。
在本文中,我们将探讨 Unicode 字符的几种常见编码方式,以及每种方式占用的字节数。
2. ASCII 编码
在 Unicode 出现之前,ASCII 是最常用的字符编码之一。
- ASCII 使用 7 位表示字符,共支持 128 个字符。
- 其中包括一些控制字符(如换行符、制表符等),真正用于显示的字符为 96 个。
- 显然,这远远无法满足 Unicode 的需求,仅适用于英文字符。
为了扩展 ASCII,出现了如 ISO-8859-1(又称为 Latin-1)等编码方式,可支持西欧语言字符。但即便如此,总共也只支持 224 个字符,仅占 Unicode 字符总数的 0.1%。
✅ 优点:单字节表示,便于存储和传输
❌ 缺点:字符集太小,无法支持多语言
3. Unicode 简介
Unicode 是一种更全面的字符表示方式。它将字符与表示方式分离,每个字符被分配一个唯一的 code point,范围从 U+000000 到 U+10FFFF。
这意味着:
- Unicode 支持的字符数量远超 ASCII
- 支持多种语言脚本(如阿拉伯语、西里尔语、泰米尔语等)
- 支持表情符号、数学符号、音乐符号等
Unicode 总共需要 21 位来表示所有字符,因此理论上需要 3 字节。但为了在不同场景下优化存储和处理效率,Unicode 提供了多种编码方式(如 UTF-8、UTF-16、UTF-32)。
⚠️ 注意:有些字符是多个 Unicode 字符组合成一个图形(如带音标的字符),但本文只讨论单个字符的编码方式。
4. UTF-32 编码
UTF-32 是最直观的 Unicode 编码方式,每个字符固定使用 4 字节(32 位)表示。
✅ 优点:
- 固定长度,便于索引和跳转
- 可直接根据字节数计算字符数
❌ 缺点:
- 浪费空间:英文字符在 ASCII 中只需 1 字节,但在 UTF-32 中需要 4 字节
- 需要考虑字节序(endianness):大端(big-endian)或小端(little-endian)
例如字符 “🂡” 的 code point 是 U+1F0A1,在 UTF-32 中表示为:
- 大端:00 01 F0 A1
- 小端:A1 F0 01 00
⚠️ 为什么使用 4 字节而不是 3?
早期 Unicode 未限制在 21 位,且 32 位整数在计算机中处理效率更高,因此选择了 4 字节。
5. UTF-8 编码
UTF-8 是目前最流行的 Unicode 编码方式,它是一种变长编码,每个字符使用 1 到 4 字节表示。
编码规则如下:
Code Point 范围 | 字节数 | 首字节格式 |
---|---|---|
U+0000 – U+007F | 1 | 0xxxxxxx |
U+0080 – U+07FF | 2 | 110xxxxx |
U+0800 – U+FFFF | 3 | 1110xxxx |
U+10000 – U+10FFFF | 4 | 11110xxx |
其余字节格式统一为:10xxxxxx
✅ 示例:字符 🂡(U+1F0A1)
- 属于 U+10000 – U+10FFFF 范围,使用 4 字节
- 首字节:
11110xxx
- 后续三字节:
10xxxxxx
code point 二进制:0000 0001 1111 0000 1010 0001
填充后:
11110000
10011111
10000010
10100001
对应十六进制:F0 9F 82 A1
✅ 示例:字符 √(U+221A)
- code point:U+221A(属于 3 字节范围)
- 原始字节:E2 88 9A
- 首字节:E2(11100010)→ 表示 3 字节
- 解码后得到:0010 0010 0001 1010 → 即 U+221A
✅ 优点
- ASCII 字符与 UTF-8 完全兼容
- 存储效率高:英文字符仅需 1 字节
- 可以从任意位置恢复字符边界
❌ 缺点
- 处理非 ASCII 字符时效率较低(如中文、泰米尔语等)
6. UTF-16 编码
UTF-16 是 Java 中字符串的默认编码方式。它使用 2 或 4 字节表示字符:
Code Point 范围 | 字节数 | 表示方式 |
---|---|---|
U+0000 – U+FFFF | 2 | 单个 16 位值 |
U+10000 – U+10FFFF | 4 | Surrogate Pair |
✅ 示例:字符 √(U+221A)
- 直接表示为:
22 1A
(2 字节)
✅ 示例:字符 🂡(U+1F0A1)
- 属于高位字符,使用 Surrogate Pair:
- 减去 0x10000 → 0xF0A1
- 高位:(0xF0A1 >> 10) + 0xD800 → 0xD83C
- 低位:(0xF0A1 & 0x3FF) + 0xDC00 → 0xDCA1
- 最终表示为:
D83C DCA1
✅ 优点
- 大多数字符使用 2 字节,节省空间
- 比 UTF-8 更容易处理大部分字符(尤其在 Java 等语言中)
❌ 缺点
- 需要处理字节序(endianness)
- ASCII 字符使用 2 字节,比 UTF-8 效率低
- Surrogate Pair 增加了实现复杂度
7. 总结
我们分析了三种常见的 Unicode 编码方式:
编码方式 | 字节长度 | 特点 |
---|---|---|
UTF-32 | 固定 4 字节 | 简单高效,但浪费空间 |
UTF-8 | 变长(1~4 字节) | 兼容 ASCII,效率高,广泛使用 |
UTF-16 | 变长(2~4 字节) | Java 默认编码,处理效率高但需处理 Surrogate Pair |
选择编码方式时应考虑:
- 数据语言(是否以英文为主)
- 存储/传输效率
- 系统平台(如 Java 使用 UTF-16)
- 是否需要兼容 ASCII
下一次处理文本存储或传输时,记得根据需求选择合适的编码方式!