字符编码全书:从零到精通

2025-08-26  本文已影响0人  NoStory

这是一份从基础到深入的实战手册,目标是让初学者一步步吃透“字符、码点、编码、存储、传输、转换、安全”等全部要点。内容覆盖我们讨论到的每个知识点,不留空缺。


一、从“字符”到“字节”:概念坐标系

核心区分:


二、U+ 表示法与最大码点


三、Unicode 的 17 个平面(Plane)

每个平面 65,536 个码点,共 17 个(0–16),总范围 U+0000–U+10FFFF。

平面号 名称 范围 主要内容 备注
0 基本多文种平面 BMP U+0000–U+FFFF 现代语言绝大多数常用字符、常见符号 含代理项区 U+D800–U+DFFF(非字符)
1 多文种补充平面 SMP U+10000–U+1FFFF 历史文字、符号、乐谱、部分 emoji 大量非 BMP 字符
2 表意文字补充平面 SIP U+20000–U+2FFFF CJK 扩展汉字(扩展 B 等) 汉字扩展
3 表意文字第三平面 TIP U+30000–U+3FFFF 更多 CJK 扩展 使用较少
4–13 保留 U+40000–U+DFFFF 预留未来分配 当前基本未分配
14 特别用途补充平面 SSP U+E0000–U+EFFFF 标签、变体选择符等 特殊用途
15 私用区 A PUA-A U+F0000–U+FFFFF 应用自定义 不在标准中定义含义
16 私用区 B PUA-B U+100000–U+10FFFF 应用自定义 不在标准中定义含义

要点:


四、UTF-8、UTF-16、UTF-32:变长与定长

UTF-8(网络事实标准)

字节数 码点范围 模式
1 U+0000–U+007F 0xxxxxxx
2 U+0080–U+07FF 110xxxxx 10xxxxxx
3 U+0800–U+FFFF(排除 U+D800–U+DFFF) 1110xxxx 10xxxxxx 10xxxxxx
4 U+10000–U+10FFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

UTF-16(广泛用于 Windows、Java/C#/JS 内部表示)

UTF-32(简单但占空间)


五、传统编码与 Unicode 的关系:GBK、Big5 等


六、标准化、合成与变体:真实世界“一个字符”有多复杂

实践建议:


七、语言与运行时中的字符串差异

语言/平台 内部存储 length 语义 索引/遍历默认单位 备注
JavaScript UTF-16 码元数 码元 for...of 按码点迭代;包含代理对 pitfalls
Java UTF-16 码元数 码元 有 codePoint API
C#/.NET UTF-16 码元数 码元 Rune/Enumerator 支持
Python 3 动态(UCS-1/2/4) 码点数 码点 大多按码点运算
Go 字符串为只读字节(UTF-8 约定) 字节数 字节 需用 range/utf8 包按 rune 遍历
Rust String 为 UTF-8 验证字节 字节数 字节 chars() 按 Unicode 标量值迭代

要点:


八、编码检测、标识与转换


九、Web、文件、数据库与工具链实务


十、安全与健壮性


十一、UTF-8 为何不是“最多 3 字节”?

常见误解是“Unicode 111 万个码点,3 字节(2^24)已经够了,为何 UTF-8 要 4 字节?”原因:


十二、代理项区与“非法码点”


十三、与旧编码打交道:策略清单


十四、工程细节与性能


十五、常用“对/错”用法对照

需求 错误做法 正确做法
统计“字符数” 直接用 JS length [...str].length 或使用按字形簇的分段器
截断可视文本 按字节/码元裁剪 按字形簇边界裁剪,保留完整 emoji/组合
存储文本 混用 GBK/UTF-8 全部 UTF-8(或明确 UTF-16),统一声明
读取文件 不指明编码“随缘” 明确 charset;无法确定就检测+回退策略
过滤输入 直接黑名单替换 解码→正规化→白名单验证
处理 UTF-8 容忍过长序列 严格拒绝过长/非法序列

十六、速查:关键数值与区段


十七、自问自答:核心问题回顾

Q: “U+10FFFF 是什么?是不是最大码点?”
A: U+10FFFF 是十六进制码点 0x10FFFF 的表示,是 Unicode 的最大合法码点。Unicode 合法范围是 U+0000–U+10FFFF,不会超过这个上限。

Q: “U+ 里的 U 和 + 各是什么意思?”
A: U 表示 Unicode;+ 没有数学意义,是固定写法前缀,后接十六进制码点。

Q: 为什么说有 17 个平面?都是什么?
A: 码点空间按每 65,536 个划成 17 个平面:Plane 0 是 BMP,Plane 1–16 是辅助平面;SMP、SIP/TIP、SSP、PUA-A/B 等用途明确,4–13 目前大多保留。范围总体 U+0000–U+10FFFF。

Q: UTF-8 和 UTF-16 都是变长,怎么判定长度?
A: UTF-8 用首字节前缀位型判定总长度,续字节以 10 开头;UTF-16 用是否命中代理对判定(BMP 内一码元;辅助平面用两个码元)。

Q: GBK、Big5 和 UTF-8 是什么关系?
A: 前两者是独立于 Unicode 的旧编码,字符集不同;UTF-8 是 Unicode 的一种编码。相互转换需“先解码为字符,再编码为目标”。不是子集/超集关系。

Q: 为什么 UTF-8 要用到 4 字节,三字节不够吗?
A: UTF-8 的前缀与自同步设计约束下,覆盖到 U+10FFFF 需要 4 字节。设计目标不仅是容量,还有兼容性、同步、排序与安全。

Q: UTF-16 的代理对到底怎么来的?
A: 码点减 0x10000 得到 20 位;高 10 位映射到 0xD800–0xDBFF,低 10 位映射到 0xDC00–0xDFFF。解码时反向合并再加回 0x10000。

Q: BMP 里也有“不能用”的码点吗?
A: 有。U+D800–U+DFFF 为代理项区,非字符;还有部分非字符与保留位,不应在互操作文本中出现。

Q: 实际工程中如何避免乱码?
A: 全链路统一编码(推荐 UTF-8),显式声明 charset;读取时指定编码;对未知来源进行检测/验证;避免 BOM/无 BOM 混用;数据库/HTTP/源文件一致。

Q: 如何按“人眼字符”遍历或截断?
A: 使用按字形簇的分段(如 ICU、Intl.Segmenter 或专业库)。不要按字节/码元/码点直接截断,以免破坏组合或 emoji 序列。

Q: 安全上要特别注意什么?
A: 严格验证 UTF-8,拒绝过长序列与代理码点;输入正规化(NFC/NFKC);警惕不可见控制字符与双向控制符;用白名单策略。

上一篇 下一篇

猜你喜欢

热点阅读