- UTF-8 使用 1-4 个字节对 Unicode 点进行编码,与 ASCII 兼容并且适用于任何语言。
- 自同步和验证:0/110/1110/11110 模式可防止重叠并更容易检测错误。
- Web 和系统:元字符集、大量支持和在 Windows/macOS/Linux 上的轻松转换。
如果您今天阅读这篇文章时没有看到任何奇怪的符号,这都要归功于UTF-8编码。这种编码方式使得字母、重音符号、技术符号,甚至表情符号,都能在任何现代浏览器、操作系统或电子邮件客户端中以相同的方式显示。它是网络上应用最广泛的标准,也是我们所知的数字通信的基础。
当设备显示文本时,它实际上是在处理数字。这些数字是由Unicode 标准定义的代码点,为了将它们转换为可以在网络上传输或保存到文件中的字节,我们需要进行一种转换:UTF-8编码。接下来,您将了解 UTF-8 编码是什么、它的工作原理、它成为标准的原因、它的优势和局限性,以及如何避免常见错误。
什么是 UTF-8?
UTF-8(8 位 Unicode 转换格式)是一种将 Unicode 代码点转换为字节序列的方法。它的主要特点是使用可变长度:有些字符占用 1 个字节,而有些字符则需要 2、3 或 4 个字节。这使得它能够表示包含简单拉丁字符的紧凑文本,但也可以表示 Unicode 字符集中的任何字符。
它与ASCII完全兼容:前 128 个字符(U+0000 到 U+007F)使用与 7 位 ASCII 相同的单个字节进行编码。这简化了从旧系统的过渡,并解释了它在互联网、电子邮件和 IETF 协议中取得巨大成功的原因。
UTF-8 的突出特点在于其稳健性:它包含同步位,可以可靠地识别每个符号的起始位置。这种自同步特性使得检测序列是否“看起来像”UTF-8 变得容易,这在工具和解析器中非常有用。
Unicode:一切的基础
Unicode 是一种通用标准,它为每个字符分配一个唯一的数字,无论语言、平台或应用程序如何。这个数字称为代码点,通常以十六进制格式 U+XXXX(或根据需要添加更多位数)书写。
例如: 大写字母“A”的 U+0041在 HTML 中我们也可以将其称为 A. 你的计算机不会将 A “视为” 一个字母,而是数字 65,然后编码(例如 UTF-8)决定如何用字节表示该数字。
如果你想查看 Unicode 如何转换为电脑上的字符,在 Windows 系统中,你可以按住 Alt 键,然后在数字键盘上输入十进制数字代码:例如,Alt+65 会返回“A”(参见完整的 Alt 代码列表)。这是一个经典的快捷方式,可以显示你看到的字符背后的代码。
一点历史:UTF-8 是如何诞生的
UTF-8 编码由 Ken Thompson 在 Rob Pike 的指导下于 1992 年 9 月 2 日设计完成。他们将其应用于贝尔实验室的 Plan 9 操作系统,并在1993 年 1 月于圣地亚哥举行的 USENIX 大会上正式发布。在由X/Open 联合国际化组织 (XOJIG)赞助的标准化过程中, UTF-8曾使用过 FSS/UTF 和 UTF-2 等名称,最终统一命名为 UTF-8。
该设计解决了以往通用编码方案中存在的诸多实际问题:ASCII 兼容性、自同步、无字节重叠以及易于错误检测。这种平衡使其成为网络的实际标准。
UTF-8 的工作原理
UTF-8 根据编码字符所需的字节数对字符进行分组。字节数完全取决于 Unicode 代码点,并遵循指示序列长度的位模式。
- 1 个字节(U+0000 至 U+007F):ASCII 字符。格式:
0xxxxxxx。最高有效位为 0,即 保证与 ASCII 直接兼容. - 2 个字节(U+0080 至 U+07FF): 格式
110yyyyy 10xxxxxx. 它适用于大多数带有变音符号的欧洲字母以及其他字母,例如希腊文、西里尔文、希伯来文或阿拉伯文。. - 3 个字节(U+0800 至 U+FFFF): 格式
1110zzzz 10yyyyyy 10xxxxxx. 包含多语言基本计划(BMP),带有 CJK(中文、日语、韩语)、技术符号和最常用的字符。 - 4 个字节(U+10000 至 U+10FFFF): 格式
11110uuu 10uuzzzz 10yyyyyy 10xxxxxx. 代表补充平面:高级数学符号、历史著作、不太常见的表意符号等。
自同步的关键在于头部位:ASCII 码为 0;两个字节为 110;三个字节为 1110;四个字节为 11110。续行字节总是以 10 开头。因此,续行字节永远不会作为起始字节出现,并且有效的序列永远不会是更长序列的子串(非重叠原则)。
与 UTF-16 和代理对的等效性
UTF-16 以 16 位为单位表示 BMP 代码点 并指向 U+FFFF 以上位置 替代对 在范围中 D800–DFFF。 相反 UTF-8 始终编码真实代码点,而不是 UTF-16 单位,这避免了与替代品混淆。
历史上,一些草案允许UTF-8 使用5 或 6 个字节来覆盖更广泛的范围,但Unicode 和 RFC 3629 将 UTF-8 限制为最多 4 个字节。ISO/IEC 曾考虑过更宽的选项,但这些选项并未纳入当前的标准。
实际例子:ñ
字符“ñ”的代码点为U+00F1,它位于双字节范围内。按照该模式,它被编码为 110xxxxx 10xxxxxx. 其 UTF-8 表示形式为 0xC3 0xB1解码是相反的过程:读取有用的位并重建原始代码点。
UTF-8 的优点和局限性
主要优势:
- ASCII 支持:ASCII 文本在 UTF-8 中有效,无需更改。
- 普遍:可以表示任何 Unicode 字符,包括技术符号和表情符号。
- 拉丁文本的效率:当使用 1 个字节表示 ASCII 时, 与 UTF-16 相比节省空间 在许多西方语言中。
- 自同步和检测:位模式允许 检测字符开头 并轻松验证序列。
局限性和权衡取舍:
- CJK 文本比 UTF-16 文本占用更多空间,其中许多字符适合 2 个固定字节。
- 计算成本:长度可变,某些操作(例如“转到字符 n”) 需要从头开始并且某些任务在 UTF-16/UTF-32 中可能会更快。
UTF-8 中的 BOM(字节顺序标记)
UTF-8 不需要 BOM 因为字节顺序不会改变值的含义(最小单位是字节)。即便如此, 有一个可选的 BOM,字符 U+FEFF 编码为 EF BB BF 在文件或流的开头,可用于指示“这是 Unicode/UTF-8”。
最佳实践:如果 BOM 出现在开头,有些系统会接受它,而有些系统则会按字面意思处理。在字符串拼接中,建议移除中间的 BOM。包含 BOM 并非强制性的,而且它在 UTF-8 编码中的作用远不如 UTF-16/UTF-32 编码(在 UTF-16/UTF-32 编码中,BOM 可以标记字节序)。
典型的编码错误及其处理方法
一个可靠的 UTF-8 解码器必须能够拒绝格式错误的序列,或者将其替换为 U+FFFD(替换字符),或者标记错误。最常见的错误包括:
- 截短序列:没有足够连续性的多字节前导字节。
- 松散的连续字节: 出现
10xxxxxx没有有效的前导字节。 - 超长:使用比必要更多的字节进行编码;例如,尝试使用 2 个字节对 ASCII 进行编码(
0xC0y0xC1无效). - 禁止的长度: 开始建议 5 或 6 个字节(
0xF8–0xFD无效 使用标准 UTF-8 编码)。 - 超出Unicode范围的值: 不支持高于 U+10FFFF 的值;某些值(
0xF5–0xF7作为开端)是无效的。 - UTF-16 代理对:
D800–DFFF不是有效的代码点 以 Unicode 表示;它们不应该以 UTF-8 编码出现。
当您在屏幕上看到字符“�”时,很可能是由于编码不匹配或文件以不同的代码页保存造成的。解决方法是强制使用端到端的 UTF-8 编码(文件、服务器、数据库、HTTP 标头)。
网络和电子邮件中的 UTF-8
一个 HTML 页面只需要声明一种编码。为了兼容性和覆盖范围,推荐使用 UTF-8 编码。请尽快在页面头部添加以下 meta 标签:
<meta charset="UTF-8">
将其放在 `<head>` 标签的开头,以便浏览器在处理文档之前读取它。这可以防止出现不一致和“乱码”。UTF -8 在网络上的普及程度非常高;绝大多数网站都使用它。
在电子邮件领域, UTF-8 编码被互联网邮件联盟 (IMC) 等组织广泛支持和推荐。将电子邮件客户端配置为使用 UTF-8 编码可以减少与使用其他语言的人交换邮件时出现的问题。
UTF-8、UTF-16 和 UTF-32:有什么区别?
UTF-8:采用 8 位单位的可变长度编码;非常适合网页设计,对 ASCII 和西方语言兼容性极佳,并具备出色的错误检测能力。
UTF-16:可变长度编码,以 16 位为单位;对于 U+10000 及以上的字符,使用代理对。当非 ASCII 字符占主导地位时,UTF-16 通常具有优势,并且被许多 API 和平台所采用(例如,Windows 本身就以 UTF-16 编码运行)。
UTF-32:每个字符长度固定为 32 位;索引非常简单,但占用空间较大。它仅适用于处理简便性比文件大小更重要的场景。
不兼容的变体:CESU-8 和“修改版 UTF-8”
CESU-8直接对 UTF-16 单元(包括代理对)进行编码,而不是对码位进行编码,因此对于 U+FFFF 以上的字符,它与标准 UTF-8 有所不同。一些早期的平台曾使用过 CESU-8:Oracle 8 以别名 UTF-8 提供CESU-8,而从 Oracle 9 开始,它又以另一个别名添加了标准 UTF-8。Java和 Tcl在某些情况下也使用过 CESU-8 。
修改后的 UTF-8 编码(例如在 Java 环境中)将空字符 (U+0000) 表示为 0xC0 0x80而不是 0x00。它避免了 C 字符串中的空字节,但并不符合 UTF-8 标准。许多这种“修改版”的实现也符合 CESU-8 标准。
Windows 和 API 上的 UTF-8:代码页和转换
Windows 内部以 UTF-16 (WCHAR) 运行,但从 Windows 10 版本 1903 开始,你可以 强制使用 UTF-8 作为进程代码页 通过应用程序清单(属性 activeCodePage)。这使得使用“-A” API 的旧代码可以更轻松地通过 UTF-8 运行。
API -A 与 -W: -A 取决于 ANSI 代码页 配置(可以是 CP_UTF8),而 -W 他们使用 UTF-16. 为了实现互操作, 的MultiByteToWideChar y 调用WideCharToMultiByte 允许您在 UTF-8 和 UTF-16 之间进行转换; 美国 CP_UTF8 并且,如果适用的话, MB_ERR_INVALID_CHARS 检测输入错误。
UTF-8编码与现代浏览器(Chrome、Firefox、Safari、Edge、Opera以及最新版本的Internet Explorer)和大多数操作系统(Windows、Linux、macOS、Android、iOS)兼容。除非您使用的是非常老旧的软件,否则应该不会遇到任何问题。
如何将文件转换为 UTF-8
在 Windows 系统中(记事本):打开文件,选择“文件 > 另存为…”,然后在“编码”中选择UTF-8。如果想保留原文件,请使用新名称另存为。
在 macOS 系统中(文本编辑):在“文本编辑 > 偏好设置 > 打开与保存”中,保存时选择Unicode (UTF-8)。然后启用该选项导出文件。
在Linux上:您可以使用终端 iconv。 例如: iconv -f <codificación_origen> -t UTF-8 <entrada> -o <salida>. 稍后检查 使用它的应用程序也需要 UTF-8。
如何判断文件是否为 UTF-8 编码?许多现代编辑器会在状态栏中显示相关信息。如果您看到诸如“�”之类的奇怪字符、损坏的重音符号或显示错误的“ñ/ç”,请检查文件编码以及编辑器/服务器/数据库设置。
避免意外的良好做法
尽早将 UTF-8 编码声明在 HTML 和 HTTP 头部中。确保整个技术栈(源文件、模板、数据库和连接)的编码一致。避免在同一页面或流程中混用不同的编码,并使用能够验证/规范化输入的工具。
对于集成和 API,始终在标题中指定编码(Content-Type: application/json; charset=UTF-8(例如)。 使用多语言数据进行测试 (重音、CJK、表情符号)在生产之前检测弱点。
UTF-8之所以胜出,是因为它兼顾了兼容性、效率和覆盖范围。无论文本是否包含重音符号、技术符号或非拉丁字母,它都是确保文本在不同文化、系统和应用程序中完整传输的最实用方法。