定长编码的取舍
UTF-32 把每个 Unicode 码位固定编成 4 字节,因此「下标即码点位置」,取第 n 个字符是常数时间,不需要状态机也不需要处理代理对。代价同样直观:纯英文内容体积变成 4 倍,中文变成 1.33 倍(原本 3 字节变 4 字节),传输与存储成本显著上升。所以它极少用于持久化,真实用途集中在内存结构、随机访问密集的场景与教学实现。
基本信息
| 单元长度 | 固定 4 字节对应一个码位 |
|---|---|
| 字节序 | 存在大端与小端,BOM 为 00 00 FE FF 或 FF FE 00 00 |
| 访问复杂度 | 按码点索引为常数时间,无需扫描 |
| 体积代价 | ASCII 膨胀 4 倍,BMP 汉字膨胀约 33% |
| 平台关联 | C/C++ 中 Linux/macOS 的 wchar_t 通常 32 位,Windows 上是 16 位 |
| 典型用途 | 内存内文本表示、随机访问索引、编码转换的中间形式 |
典型使用场景
- 算法实现与调试:需要按码位随机访问或做区间统计时,定长表示让代码更简单
- 文本编辑器与编译器的内部索引:内部用码点或 UTF-32 便于按列定位,落盘仍用 UTF-8
- 库接口适配:部分国际化库以 32 位码点类型传参,编译期做一次转换后即用即弃
- 编码映射验证:拿 UTF-32 作为参照,验证自定义 UTF-8 编解码实现是否正确
- 排查异常文本:把可疑内容转成 U+XXXX 列表,逐码点确认到底存进了什么
转换与观察示例
转换与观察示例
python -c "print(len('A'.encode('utf-32')), len('中'.encode('utf-32')))" # 均为 4 字节(不含 BOM)
python -c "b='A'.encode('utf-32'); print(b)" # 默认小端且带 BOM
python -c "print('\U0001F600'.encode('utf-32-le'))" # 显式小端,无 BOM
iconv -f UTF-8 -t UTF-32LE file.txt -o out.txt # 命令行转换,落盘时通常选 LE
xxd -l 16 out.txt # 看是否有 00 00 FE FF 之类的 BOM
# 调试用法:把字符串按码点打印,快速发现不可见字符
python -c "print([hex(ord(c)) for c in open('bad.txt', encoding='utf-8').read()[:10]])"三种 UTF 编码对比
| 内容 | UTF-8 | UTF-16 | UTF-32 |
|---|---|---|---|
| ASCII 字符 | 1 字节 | 2 字节 | 4 字节 |
| 常用汉字 | 3 字节 | 2 字节 | 4 字节 |
| emoji 等增补平面 | 4 字节 | 4 字节(代理对) | 4 字节 |
| 随机访问 | 需扫描 | 需处理代理对 | 常数时间 |
| 落盘与传输 | 首选 | 少见 | 几乎不用 |
| BOM 必要性 | 不需要 | 需要(无外部声明时) | 需要(无外部声明时) |
UTF-32 与「用 32 位整数保存码点」不完全是一回事:内存中的码点数组没有字节序也没有 BOM,只有在序列化成字节流时才需要考虑端序。很多库的接口用 32 位码点类型传参,这不是在做 UTF-32 编码。
UCS-4 是早期用 4 字节表示「所有 ISO 10646 码位」的方案,理论上可表示更大范围;UTF-32 只覆盖 Unicode 实际的码位范围(不超过 U+10FFFF,且不含代理区)。两者字节宽度相同但合法取值集合不同,接口约定时不要混用名称。
不要假设 wchar_t 就是 32 位:Windows 上是 16 位并沿用 UTF-16 语义,Linux/macOS 上通常是 32 位。跨平台 C/C++ 代码若按 wchar_t 宽度写字符串处理逻辑,会在 Windows 上遇到代理对问题。