UTF-8
Unicode 的可变长度编码方式。作为 Web 的标准字符编码被广泛使用。
UTF-8 是为实现 Unicode 而设计的可变长度字符编码。它在 RFC 3629 (2003 年 11 月,Internet Standard STD 63) 中被定义为把 U+0000 到 U+10FFFF 的字符编码为 1 至 4 个字节的序列的方式。它在与 ASCII (英文字母、数字、符号) 保持完全兼容的同时,能够表示 Unicode 所收录的字符。根据 W3Techs 的使用状况调查,在能够判明字符编码的网站中,截至 2026 年 8 月有 99.0% 使用 UTF-8,它是 Web 上事实上的标准字符编码。
UTF-8 的字节数因字符的种类而不同。ASCII (U+0000 至 U+007F) 为 1 字节,拉丁字母的扩展和西里尔字母 (U+0080 至 U+07FF) 为 2 字节,日语、中文、韩语的汉字以及平假名、片假名 (U+0800 至 U+FFFF) 为 3 字节,表情符号和 CJK 统一汉字扩展区的汉字 (U+10000 至 U+10FFFF) 为 4 字节。另外,为 UTF-16 的代理项而保留的 U+D800 至 U+DFFF,在 UTF-8 中无法编码。凭借这种可变长度的设计,英文文本能以与 ASCII 相同的体积存放,多语言文本也能表示得没有浪费。
UTF-8 成为 Web 标准的原因有多个。由于具备 ASCII 兼容性,既有的以 ASCII 为基础的协议 (HTTP、SMTP、URL) 和工具可以原样运作。它不存在字节序 (端序) 的问题,也不需要 BOM (字节顺序标记)。此外它还具有自同步性,即便从字节序列的中途也能确定字符的边界,是一种对数据损坏有较强抵抗力的设计。实务中容易被绊住的是混入了带 BOM 保存的文件的情形。由于开头会进入看不见的 3 个字节 (十六进制为 EF BB BF),就会出现配置文件或 JSON 解析失败、CSV 第 1 列的表头名对不上之类的症状。
在 HTML 中用 <meta charset="UTF-8"> 声明字符编码。没有这个声明,浏览器就会误判字符编码,成为乱码的原因。也可以用 HTTP 响应头 Content-Type: text/html; charset=utf-8 指定,两处都写上的话,单独打开文件时声明也依然有效。这里的判断依据是优先顺序。在浏览器的判定中,响应头的指定会先于 meta 被采用,因此两者互相矛盾时,无论怎么修改 meta 标签,显示都不会改变。追查乱码时,要先确认响应头返回的是什么。
在数据库中,UTF-8 的处理需要留意。MySQL 的 utf8 是最多只能处理 3 个字节的 utf8mb3 的别名,在截至 2026 年 8 月的 8.4 系参考手册中,这个别名和 utf8mb3 本身都被列为不推荐使用,并说明预计将在未来的主版本发布中删除。推荐使用的是 utf8mb4。在只到 3 个字节的字符集里无法存放表情符号或「𠀋」这类扩展区的汉字,登记时会报错或者字符丢失。如果设计上要接收含有人名异体字或表情符号的投稿,那么在初始设置时就选好 utf8mb4 比投入运行后再转换更为安全。PostgreSQL 的 UTF8 从一开始就支持 4 个字节。
就与字数统计的关联而言,理解 UTF-8 的字节数与字符数之别很重要。日语的「こんにちは」是 5 个字符,但在 UTF-8 中是 15 个字节 (3 字节 × 5 个字符)。若混入 1 个表情符号,字符数只增加 1,字节数却增加 4。数据库的列大小、文件大小以及 API 的请求上限多以字节数指定,因此只看字符数来做设计的话,要等到跨过上限的那一刻才第一次被挡下来。可以按"用日语写成的文章,其字节数约为字符数的 3 倍"来估算,含有表情符号或扩展区汉字时再多留一些余量,这样就能避免投稿和保存的失败。