ASCII vs Unicode
ASCII 是 1960 年代的字符集,定义了 128 个字符:英文字母、数字、基本标点、33 个控制字符。Unicode 是现代字符集,目标是对每种活的语言里的每个字符都编码——目前大约 154,000 个,还在增长。
几乎所有现代系统底层都用 Unicode。ASCII 是历史基线,恰好落在 Unicode 码点的前 128 个之内:每个 ASCII 文件都是合法的 UTF-8 文件。
ASCII 用在哪里
ASCII 给可打印字符分配了字节值 0-127。值 128-255 后来被各种"扩展 ASCII"编码占用(Latin-1、Windows-1252、Shift-JIS 等)——每家各自加重音符号、货币符号或 CJK 字符。由此产生了经典的"乱码"问题:一个系统写入的文件,在另一个系统打开,显示出 é 而不是 é。
Web 和现代文件格式(JSON、XML、YAML、Markdown、现代 TXT)一致选用 UTF-8 作为默认,这个问题在很大程度上被消除。老编码仍会出现在旧数据里:很多老 Excel 安装版导出的 .csv 仍然用 Windows-1252。
UTF-8 vs UTF-16 vs UTF-32
Unicode 有多种序列化格式:
- UTF-8 — 可变宽(每码点 1-4 字节),与 ASCII 兼容(前 128 字节与 ASCII 完全相同)。Web、所有主流操作系统和绝大多数文件格式的默认。
- UTF-16 — 可变宽(2 或 4 字节)。Windows、Java、JavaScript 字符串在内部使用(历史上如此;现代 JS 引擎内部也可能用 UTF-8)。
- UTF-32 — 固定宽(每码点 4 字节)。简单但对 ASCII 占多数的数据浪费;某些需要快速随机访问码点的系统在内部使用。
现代语境中,系统说"Unicode"而不指明,几乎总是指 UTF-8。
对正则意味着什么
代码里的正则引擎按 locale 或编码标志来解释字节序列。. 在 ASCII 模式下匹配"除换行外的任意字符",在 Unicode 模式下匹配"任意码点"。\w(字字符)在 Unicode 模式下包含的字母多得多——A-Z 和 a-z 之外,还有 é、ñ、Ω、任何 CJK 表意字,等等。
现代正则引擎默认 Unicode 模式。想要纯 ASCII 行为,需要显式标志(比如用 [A-Za-z] 而不是 \w)。
一个实际例子:emoji
“😀”(U+1F600,“露齿笑的脸”)是一个 Unicode 码点,在 UTF-8 中占 4 字节(F0 9F 98 80)。从 Unicode 角度看是 1 个"字符",从 UTF-8 字节角度看是 4 个字节。
这就是为什么 JavaScript 里的 “length” 把 U+1F600 计为 2(它考虑了 UTF-16 代理对)。JS 的 string.length 操作的是 UTF-16 code unit,不是码点。
工具
要检视任何粘贴字符串的字节和码点——包括不可见字符和 Unicode 转义——用 Unicode Inspector。它在你的浏览器里跑,不传输任何东西。
对于混合 ASCII 和 Unicode 的文本(大多数真实文档),Case Converter 工具处理跨文字的大小写规则。
另见
- 正则速查表 — 当真正的关注点是 Unicode 感知的正则时
- PDF to Text — 从含混合编码的 PDF 提取文字
- JSON to CSV — 当字符编码决定你的 CSV 是否能正确打开