什么是 UTF-8
本文约 4 分钟读完。
网页、API、邮件头、数据库 - 现代字符编码的事实标准就是 UTF-8。它以 1 至 4 字节的可变长方式表示 Unicode 码位,在保持与 ASCII 向下兼容的同时, 能够处理包括 emoji 在内的所有 Unicode 字符。它在我们平时不太注意的地方,作为数字文字的流通基础设施发挥着作用。
定义
UTF-8 (8-bit Unicode Transformation Format) 是将 Unicode 码位转换为字节序列的编码方式之一。 由 Ken Thompson 和 Rob Pike 于 1992 年设计,后来作为 RFC 3629 标准化。 它以每个字符 1 至 4 字节的可变长度表示 Unicode 全范围 (U+0000 到 U+10FFFF)。
字节结构
| 码位范围 | 字节数 | 示例 |
|---|---|---|
| U+0000 - U+007F | 1 字节 | ASCII (a, A, 0, !) |
| U+0080 - U+07FF | 2 字节 | 拉丁扩展、希腊、西里尔 |
| U+0800 - U+FFFF | 3 字节 | 日语、中文、韩语 |
| U+10000 - U+10FFFF | 4 字节 | emoji、古文字、补充汉字 |
与 ASCII 的兼容性
UTF-8 的一个重要设计特点是,ASCII (U+0000 - U+007F) 直接用 1 字节表示。 仅由英文数字和半角符号组成的文件,无论作为 ASCII 还是 UTF-8 解读,字节序列都是相同的。 正是这种让现有 ASCII 文本处理工具能够无损处理 UTF-8 文件的向后兼容性,推动了它在 Web 上的普及。
与 UTF-16 / UTF-32 的比较
| 方式 | 每个字符 | 主要用途 | emoji 的处理 |
|---|---|---|---|
| UTF-8 | 1 至 4 字节 | Web、API、文件 | 4 字节表示 |
| UTF-16 | 2 或 4 字节 | Java、JS 的内部表示 | 代理对 4 字节 |
| UTF-32 | 固定 4 字节 | 内部处理 (少见) | 固定 4 字节 |
作为 Web 标准的地位
W3C 在 HTML5 中推荐使用 UTF-8 作为字符编码,现代网页中超过 98% 使用 UTF-8 分发。 HTTP 头、JSON、XML、邮件正文、文件名、URL 的百分号编码 - Web 的各个角落都以 UTF-8 为前提。 新项目几乎没有选择 UTF-8 以外编码的理由。
BOM (字节顺序标记)
有一种做法是在 UTF-8 文件开头加上 EF BB BF 这 3 个字节的 BOM, 但 Web 标准不推荐使用 UTF-8 BOM。带 BOM 的 UTF-8 由旧版 Windows 工具 (如记事本) 生成, 在 Linux / Mac 工具和 Web 服务器中可能被当作意外字节而引发问题。 现代做法是保存文件时不加 BOM。
实务注意事项
- 字符数与字节数的混淆: 一个中文字符在 UTF-8 中占 3 字节,emoji 占 4 字节。用字节数判断长度会在多字节字符上产生混乱
- 数据库字符集: MySQL 的
utf8实际上只能处理最多 3 字节,是不完整的实现。要处理 emoji 需选择utf8mb4 - URL 编码: 在 URL 中包含中文或 emoji 时,会将 UTF-8 字节序列进行百分号编码
- 文件读取: 如果错误地以 Shift_JIS 打开 UTF-8 文件会出现乱码 (常见于旧 CSV 文件)
常见误解
- ❌「UTF-8 是 8 位固定长度」→ ✅ 以 8 位为最小单位的可变长编码
- ❌「UTF-8 比 UTF-16 慢」→ ✅ 取决于内容和处理方式。中日韩文字多时 UTF-16 可能更短
- ❌「BOM 是必须的」→ ✅ UTF-8 中不加 BOM 才是标准做法