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:
    1. 减去 0x10000 → 0xF0A1
    2. 高位:(0xF0A1 >> 10) + 0xD800 → 0xD83C
    3. 低位:(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

下一次处理文本存储或传输时,记得根据需求选择合适的编码方式!


原始标题:How Many Bytes Does One Unicode Character Take?