知识库 · 文本、数据与安全
Unicode 和 UTF-8 有什么区别?
简短回答
Unicode 是字符集:它给每个字符分配一个编号,叫作码点,例如「é」是 U+00E9。UTF-8 是编码方式:把这些编号存成字节的规则。ASCII 字符占 1 个字节,其他字符占 2 到 4 个字节,所以「é」会变成 C3 A9。Unicode 规定是哪个字符,UTF-8 规定它如何写入磁盘或通过网络传输。
先有码点,再有字节
Unicode 定义的码位空间从 U+0000 到 U+10FFFF,共可容纳 1,114,112 个码点,分为 17 个平面,每个平面 65,536 个。其中已有超过 150,000 个分配给了字符,涵盖现代和历史文字、符号以及 emoji。码点写作 U+ 加至少四位十六进制数字。
码点只是一个数字。要保存或传输文本,就必须把它转换成字节,而转换方法有好几种:UTF-8、UTF-16 和 UTF-32 都是同一套 Unicode 字符的不同编码方式。
| 字符 | 码点 | UTF-8 字节 | UTF-16(大端序) |
|---|---|---|---|
| A | U+0041 | 41 | 00 41 |
| é | U+00E9 | C3 A9 | 00 E9 |
| € | U+20AC | E2 82 AC | 20 AC |
| 日 | U+65E5 | E6 97 A5 | 65 E5 |
| 😀 | U+1F600 | F0 9F 98 80 | D8 3D DE 00 |
UTF-8 如何决定用 1、2、3 还是 4 个字节
UTF-8 由 RFC 3629 规定,是一种变长编码。首字节的前几位表示后面还跟几个字节,每个后续字节都以 10 开头。这让字节流具有自同步性:解码器即使从某个字符中间开始读,也能跳到下一个起始字节。
| 码点范围 | 字节数 | 位模式 |
|---|---|---|
| U+0000 – U+007F | 1 | 0xxxxxxx |
| U+0080 – U+07FF | 2 | 110xxxxx 10xxxxxx |
| U+0800 – U+FFFF | 3 | 1110xxxx 10xxxxxx 10xxxxxx |
| U+10000 – U+10FFFF | 4 | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx |
计算示例:「é」是 U+00E9,二进制为 000 1110 1001,需要用 2 字节模式。高 5 位 00011 填入 110xxxxx,得到 11000011(C3);低 6 位 101001 填入 10xxxxxx,得到 10101001(A9)。
前 128 个码点编码后与 ASCII 的单字节完全相同,所以任何合法的 ASCII 文件本身就是合法的 UTF-8。这种向后兼容性是 UTF-8 成为网页默认编码的主要原因,目前约 98% 的网站使用 UTF-8。
UTF-8、UTF-16 和 UTF-32 对比
| 编码 | 码元大小 | 每个字符的字节数 | 常见于 |
|---|---|---|---|
| UTF-8 | 8 位 | 1–4 | 网页、JSON、Linux 和 macOS 文件、大多数 API |
| UTF-16 | 16 位 | 2 或 4 | JavaScript 字符串、Java、Windows API |
| UTF-32 | 32 位 | 固定 4 | 需要定长的内部处理 |
UTF-16 把 U+FFFF 以上的字符存成由两个 16 位码元组成的代理对。所以在 JavaScript 中 "😀".length 是 2:这门语言统计的是 UTF-16 码元,而不是字符。用 [..."😀"].length 遍历则返回 1。
对英文文本来说,UTF-8 最紧凑。对以中文、日文或韩文为主的文本,UTF-16 可能更小,因为这些字符在 UTF-8 中占 3 个字节,在 UTF-16 中只占 2 个。
字节顺序标记(BOM)
BOM 是放在文件最开头的字符 U+FEFF。在 UTF-16 和 UTF-32 中,它告诉读取方字节是大端序还是小端序。在 UTF-8 中它编码为 EF BB BF,只起签名作用,因为 UTF-8 没有字节序问题。
Unicode FAQ 指出,UTF-8 BOM 既不必要也不推荐。但有些 Windows 编辑器还是会加上它,结果会破坏 shell 脚本(#! 行不再位于开头)、CSV 表头,以及不预期 BOM 的 JSON 解析器。如果文件开头显示 ,那就是被当成 Windows-1252 读取的 UTF-8 BOM。
文字为什么会变成 é 或 ���
乱码(mojibake)出现的原因,是用来解码字节的编码与写入时的编码不一致:
é显示成é:UTF-8 字节C3 A9被当作 Windows-1252 或 Latin-1 读取,C3对应Ã,A9对应©。�(U+FFFD,替换字符):解码器遇到了不合法的 UTF-8 字节,常见于把 Latin-1 文本当作 UTF-8 读取。- 数据库里出现问号:列或连接使用的字符集无法容纳该字符,写入时就被替换掉了,原字符已经丢失。
解决办法是在每个环节都声明编码:HTML 中的 <meta charset="utf-8">,HTTP 头中的 Content-Type: text/html; charset=utf-8,数据库列和连接也都用 UTF-8。MySQL 请使用 utf8mb4;旧的 utf8 字符集每个字符最多存 3 个字节,无法保存 emoji。
常见问题
UTF-8 就是 Unicode 吗?
不是。Unicode 是字符及其编号的清单,UTF-8 是把这些编号存成字节的一种方式。人们说「Unicode」时,常常指的是 UTF-8 文本。
UTF-8 中一个字符占几个字节?
1 到 4 个。ASCII 字母占 1 个字节,大多数欧洲带重音字母占 2 个,大多数中文、日文和韩文字符占 3 个,emoji 占 4 个。
UTF-8 能表示所有字符吗?
能。UTF-8 可以编码从 U+0000 到 U+10FFFF 的所有 Unicode 码点,所以 Unicode 定义的任何字符都能用 UTF-8 写出来。
保存文件时要不要带 BOM?
对 UTF-8 来说,通常不需要。BOM 是可选的,对字节序毫无作用,还会干扰那些认为开头字节就是正文的工具。不过某些旧版 Excel 需要 BOM 才能正确打开 UTF-8 编码的 CSV 文件。