1. 前言
UTF-8 编码是交换 Unicode(通用编码字符集)最合适的编码。因此,对于新协议和格式,以及在新环境中部署的现有格式,本规范要求(并定义)使用 UTF-8 编码。
其他(旧)编码在过去某种程度上已经被定义。然而,用户代理并未始终以相同的方式实现它们,也未始终使用相同的标签,并且在处理编码的未定义和以前的专有区域时经常有所不同。本规范解决了这些差距,使得新的用户代理无需逆向工程编码实现,而现有的用户代理可以趋于一致。
特别是,本规范定义了所有这些编码、从字节到标量值及其返回的算法、它们的规范名称和识别标签。本规范还定义了一个 API,用于向 JavaScript 暴露部分编码算法。
用户代理在标签方面也显著偏离了 IANA 字符集注册表中列出的内容。为了阻止进一步传播旧编码,本规范对上述细节进行了详尽的定义,因此不需要注册表。特别是,本规范不提供扩展编码任何方面的机制。
2. 安全背景
当生产者和消费者对使用的编码或对给定编码的实现方式未达成一致时,会出现一系列编码安全问题。例如,2011 年报告了一次攻击,其中 Shift_JIS 的前导字节 0x82 被用来“掩盖” JSON 资源中某字段的尾随字节 0x22,该字段由攻击者控制。生产者未发现问题,尽管这是一个非法字节组合。消费者将其解码为单个 U+FFFD(�),因此改变了整体解释,因为 U+0022(")是一个重要的分隔符。现在,使用多个字节表示标量值的编码的解码器要求,在非法字节组合的情况下,范围 U+0000 到 U+007F(含)的标量值不能被“掩盖”。对于上述序列,输出将是 U+FFFD U+0022。(作为一个不幸的例外,gb18030 解码器将在 队列末尾最多“掩盖”一个这样的字节。)
对于将任何 ASCII 字节映射到非 ASCII 代码点的编码(当没有前导字节存在时),这是一个更大的问题。这些是“ASCII 不兼容”编码,除了由于已部署内容而必须支持的 ISO-2022-JP 和 UTF-16BE/LE,它们不被支持。(正在 调查是否可以将更多此类编码的标签映射到 替代编码,而不是未知编码回退。)一个示例攻击是向资源注入精心设计的内容,然后鼓励用户覆盖编码,从而导致例如脚本执行。
HTML 和 HTML 表单功能中使用的 URL 编码器在使用无法表示所有标量值的编码时也可能导致轻微的信息丢失。例如,当资源使用 windows-1252 编码时,服务器将无法区分最终用户在表单中输入“💩”和“💩”。
当仅使用 UTF-8 时,这里概述的问题将消失,这也是现在所有事物必须使用 UTF-8 编码的众多原因之一。
另请参阅 浏览器 UI章节。
3. 术语
本规范依赖于 Infra 标准。[INFRA]
十六进制数字以 "0x" 为前缀。
在公式中,所有数字均为整数,加法用 "+" 表示,减法用 "−" 表示,乘法用 "×" 表示,整数除法用 "/" 表示(返回商),取模用 "%" 表示(返回整数除法的余数),逻辑左移用 "<<" 表示,逻辑右移用 ">>" 表示,按位与用 "&" 表示,按位或用 "|" 表示。
对于逻辑右移,操作数必须至少具有 21 位精度。
I/O 队列 是一种 列表,其 项具有特定类型(例如 字节或 标量值)。队列末尾 是一种特殊的 项,可以存在于任何类型的 I/O 队列中,表示队列中没有更多的 项。
使用 I/O 队列有两种方式:即时模式,用于表示存储在内存中的 I/O 数据;流模式,用于表示来自网络的数据。即时队列的最后一项是 队列末尾,而流队列不需要有它,因此它们的 读取操作可能会阻塞。
预计流式 I/O 队列将被创建为空,并且随着数据从网络进入,会将新的 项 推入队列。当底层网络流关闭时,应将一个 队列末尾项 推入队列。
要从 I/O 队列 ioQueue 读取一个 项,按以下步骤执行:
-
如果 ioQueue[0] 是 队列结尾(end-of-queue),则返回 队列结尾。
-
移除 ioQueue[0] 并返回它。
要从 ioQueue 中读取 number 个 项,请执行以下步骤:
向 I/O 队列 ioQueue 推送一系列项,是指按照给定顺序,将序列中的每一项依次推送到 ioQueue 中。
将一个 项(非 队列结尾(end-of-queue))恢复到 I/O 队列中,即执行 列表的 前插(prepend)操作。将一个不含 队列结尾 的项列表恢复到 I/O 队列中,则按给定顺序将这些项插入到队列的第一个项之前。
如果向 I/O 队列 « 0x92 0xA9, 队列结尾 » 插入字节 « 0xF0, 0x9F », 则结果为 I/O 队列 « 0xF0, 0x9F, 0x92 0xA9, 队列结尾 »。下一个要读取的项是 0xF0。
Infra 标准预计将定义一些关于类型转换的基础设施。详见 whatwg/infra issue #319。[INFRA]
I/O 队列被定义为列表,而非队列,因为它们包含 恢复操作。然而,这一恢复操作仅是本规范算法的内部细节,不应被其他标准使用。实现可以采用其他方式来实现此类算法,细节见实现注意事项。
要从代理对中获取一个 标量值,给定一个 前导代理 leading 和一个 尾随代理 trailing,返回 0x10000 + ((leading − 0xD800) << 10) + (trailing − 0xDC00)。
要创建一个
Uint8Array
对象,给定一个 I/O 队列 ioQueue 和 realm realm:
-
令 bytes 为将 ioQueue 转换为字节序列的结果。
-
返回在 realm 中用 bytes 创建
Uint8Array
对象的结果。
4. 编码
编码定义了从标量值序列到字节序列(及其反向)的映射。每个编码都有一个名称,以及一个或多个标签。
本规范定义了三个与 Unicode 标准中定义的编码方案同名的编码:UTF-8、UTF-16LE和UTF-16BE。这些编码与编码方案的不同之处在于字节顺序标记(也称为 BOM)的处理不是编码本身的一部分,而是本规范中包装算法的一部分,而字节顺序标记处理是 Unicode 标准中编码方案定义的一部分。UTF-8与UTF-8 解码算法一起使用时,与同名的编码方案匹配。本规范不提供与UTF-16LE和UTF-16BE结合以匹配类似名称的编码方案的包装算法。[UNICODE]
4.1. 编码器和解码器
每个编码都有一个关联的解码器,并且大多数都有一个关联的编码器。解码器和编码器的实例具有一个处理器算法,并可能具有状态。处理器算法接受一个输入I/O 队列和一个项,并返回完成、一个或多个项、错误(可选地带有一个代码点),或继续。
错误模式在以下使用中,对于解码器是"replacement
"或"fatal
",对于编码器是"fatal
"或"html
"。
由于 HTML 表单需要一个非终止的旧编码器,"html
"作为错误模式存在。"html
"错误模式会导致发出一个无法与合法输入区分的序列,因此可能导致静默的数据丢失。强烈建议开发人员使用UTF-8编码以防止这种情况发生。[HTML]
要处理一个项,给定一个项item、一个编码的编码器或解码器实例encoderDecoder、I/O 队列input、I/O 队列output和错误模式mode:
4.2. 名称和标签
下表列出了所有编码及其标签,用户代理必须支持这些编码和标签。用户代理不得支持任何其他编码或标签。
对于每个编码,对其ASCII 小写化的名称会生成其一个标签。
作者必须使用UTF-8编码,并且必须使用其(ASCII
大小写不敏感)"utf-8
"标签来标识它。
新协议和格式,以及在新环境中部署的现有格式,必须仅使用UTF-8编码。如果这些协议和格式需要公开编码的名称或标签,它们必须将其公开为"utf-8
"。
要从字符串label获取编码,请执行以下步骤:
-
移除label中的任何前导和尾随ASCII 空白。
-
如果label与下表中列出的任何标签ASCII 大小写不敏感匹配,则返回对应的编码;否则返回失败。
这是一个比Unicode 技术标准第 22 号第 1.4 节规定的更基本和更严格的将标签映射到编码的算法,因为这是与已部署内容兼容所必需的。
名称 | 标签 |
---|---|
编码 | |
UTF-8 | "unicode-1-1-utf-8 " |
"unicode11utf8 " |
|
"unicode20utf8 " |
|
"utf-8 " |
|
"utf8 " |
|
"x-unicode20utf8 " |
|
旧单字节编码 | |
IBM866 | "866 " |
"cp866 " |
|
"csibm866 " |
|
"ibm866 " |
|
ISO-8859-2 | "csisolatin2 " |
"iso-8859-2 " |
|
"iso-ir-101 " |
|
"iso8859-2 " |
|
"iso88592 " |
|
"iso_8859-2 " |
|
"iso_8859-2:1987 " |
|
"l2 " |
|
"latin2 "
| |
ISO-8859-3 | "csisolatin3 "
|
"iso-8859-3 "
| |
"iso-ir-109 "
| |
"iso8859-3 "
| |
"iso88593 "
| |
"iso_8859-3 "
| |
"iso_8859-3:1988 "
| |
"l3 "
| |
"latin3 "
| |
ISO-8859-4 | "csisolatin4 "
|
"iso-8859-4 "
| |
"iso-ir-110 "
| |
"iso8859-4 "
| |
"iso88594 "
| |
"iso_8859-4 "
| |
"iso_8859-4:1988 "
| |
"l4 "
| |
"latin4 "
| |
ISO-8859-5 | "csisolatincyrillic "
|
"cyrillic "
| |
"iso-8859-5 "
| |
"iso-ir-144 "
| |
"iso8859-5 "
| |
"iso88595 "
| |
"iso_8859-5 "
| |
"iso_8859-5:1988 "
| |
ISO-8859-6 | "arabic "
|
"asmo-708 "
| |
"csiso88596e "
| |
"csiso88596i "
| |
"csisolatinarabic "
| |
"ecma-114 "
| |
"iso-8859-6 "
| |
"iso-8859-6-e "
| |
"iso-8859-6-i "
| |
"iso-ir-127 "
| |
"iso8859-6 "
| |
"iso88596 "
| |
"iso_8859-6 "
| |
"iso_8859-6:1987 "
| |
ISO-8859-7 | "csisolatingreek "
|
"ecma-118 "
| |
"elot_928 "
| |
"greek "
| |
"greek8 "
| |
"iso-8859-7 "
| |
"iso-ir-126 "
| |
"iso8859-7 "
| |
"iso88597 "
| |
"iso_8859-7 "
| |
"iso_8859-7:1987 "
| |
"sun_eu_greek "
| |
ISO-8859-8 | "csiso88598e "
|
"csisolatinhebrew "
| |
"hebrew "
| |
"iso-8859-8 "
| |
"iso-8859-8-e "
| |
"iso-ir-138 "
| |
"iso8859-8 "
| |
"iso88598 "
| |
"iso_8859-8 "
| |
"iso_8859-8:1988 "
| |
"visual "
| |
ISO-8859-8-I | "csiso88598i "
|
"iso-8859-8-i "
| |
"logical "
| |
ISO-8859-10 | "csisolatin6 "
|
"iso-8859-10 "
| |
"iso-ir-157 "
| |
"iso8859-10 "
| |
"iso885910 "
| |
"l6 "
| |
"latin6 "
| |
ISO-8859-13 | "iso-8859-13 "
|
"iso8859-13 "
| |
"iso885913 "
| |
ISO-8859-14 | "iso-8859-14 "
|
"iso8859-14 "
| |
"iso885914 "
| |
ISO-8859-15 | "csisolatin9 "
|
"iso-8859-15 "
| |
"iso8859-15 "
| |
"iso885915 "
| |
"iso_8859-15 "
| |
"l9 "
| |
ISO-8859-16 | "iso-8859-16 "
|
KOI8-R | "cskoi8r "
|
"koi "
| |
"koi8 "
| |
"koi8-r "
| |
"koi8_r "
| |
KOI8-U | "koi8-ru "
|
"koi8-u "
| |
macintosh | "csmacintosh "
|
"mac "
| |
"macintosh "
| |
"x-mac-roman "
| |
windows-874 | "dos-874 "
|
"iso-8859-11 "
| |
"iso8859-11 "
| |
"iso885911 "
| |
"tis-620 "
| |
"windows-874 "
| |
windows-1250 | "cp1250 "
|
"windows-1250 "
| |
"x-cp1250 "
| |
windows-1251 | "cp1251 "
|
"windows-1251 "
| |
"x-cp1251 "
| |
windows-1252
参见下文了解与历史上的“Latin1”和“ASCII”概念的关系。 | "ansi_x3.4-1968 "
|
"ascii "
| |
"cp1252 "
| |
"cp819 "
| |
"csisolatin1 "
| |
"ibm819 "
| |
"iso-8859-1 "
| |
"iso-ir-100 "
| |
"iso8859-1 "
| |
"iso88591 "
| |
"iso_8859-1 "
| |
"iso_8859-1:1987 "
| |
"l1 "
| |
"latin1 "
| |
"us-ascii "
| |
"windows-1252 "
| |
"x-cp1252 "
| |
windows-1253 | "cp1253 "
|
"windows-1253 "
| |
"x-cp1253 "
| |
windows-1254 | "cp1254 "
|
"csisolatin5 "
| |
"iso-8859-9 "
| |
"iso-ir-148 "
| |
"iso8859-9 "
| |
"iso88599 "
| |
"iso_8859-9 "
| |
"iso_8859-9:1989 "
| |
"l5 "
| |
"latin5 "
| |
"windows-1254 "
| |
"x-cp1254 "
| |
windows-1255 | "cp1255 "
|
"windows-1255 "
| |
"x-cp1255 "
| |
windows-1256 | "cp1256 "
|
"windows-1256 "
| |
"x-cp1256 "
| |
windows-1257 | "cp1257 "
|
"windows-1257 "
| |
"x-cp1257 "
| |
windows-1258 | "cp1258 "
|
"windows-1258 "
| |
"x-cp1258 "
| |
x-mac-cyrillic | "x-mac-cyrillic "
|
"x-mac-ukrainian "
| |
旧多字节中文(简体)编码 | |
GBK | "chinese "
|
"csgb2312 "
| |
"csiso58gb231280 "
| |
"gb2312 "
| |
"gb_2312 "
| |
"gb_2312-80 "
| |
"gbk "
| |
"iso-ir-58 "
| |
"x-gbk "
| |
gb18030 | "gb18030 "
|
旧多字节中文(繁体)编码 | |
Big5 | "big5 "
|
"big5-hkscs "
| |
"cn-big5 "
| |
"csbig5 "
| |
"x-x-big5 "
| |
旧多字节日语编码 | |
EUC-JP | "cseucpkdfmtjapanese "
|
"euc-jp "
| |
"x-euc-jp "
| |
ISO-2022-JP | "csiso2022jp "
|
"iso-2022-jp "
| |
Shift_JIS | "csshiftjis "
|
"ms932 "
| |
"ms_kanji "
| |
"shift-jis "
| |
"shift_jis "
| |
"sjis "
| |
"windows-31j "
| |
"x-sjis "
| |
旧多字节韩语编码 | |
EUC-KR | "cseuckr "
|
"csksc56011987 "
| |
"euc-kr "
| |
"iso-ir-149 "
| |
"korean "
| |
"ks_c_5601-1987 "
| |
"ks_c_5601-1989 "
| |
"ksc5601 "
| |
"ksc_5601 "
| |
"windows-949 "
| |
旧杂项编码 | |
replacement | "csiso2022kr "
|
"hz-gb-2312 "
| |
"iso-2022-cn "
| |
"iso-2022-cn-ext "
| |
"iso-2022-kr "
| |
"replacement "
| |
UTF-16BE | "unicodefffe "
|
"utf-16be "
| |
UTF-16LE | "csunicode "
|
"iso-10646-ucs-2 "
| |
"ucs-2 "
| |
"unicode "
| |
"unicodefeff "
| |
"utf-16 "
| |
"utf-16le "
| |
x-user-defined | "x-user-defined "
|
所有编码及其标签也可以作为非规范性的encodings.json资源获取。
支持的编码集主要基于本标准开发开始时主要浏览器引擎支持的编码集的交集,同时移除了那些很少被合法使用但可能被用于攻击的编码。从现有 Web 内容的使用水平的零散证据来看,某些编码的包含是值得怀疑的。也就是说,虽然这些编码被浏览器广泛支持,但尚不清楚它们是否被 Web 内容广泛使用。然而,并未积极移除那些被浏览器广泛支持或属于 ISO 8859 系列的单字节编码。特别是,IBM866、macintosh、x-mac-cyrillic、ISO-8859-3、ISO-8859-10、ISO-8859-14和ISO-8859-16的必要性对于支持现有内容来说是值得怀疑的,但目前没有移除这些编码的计划。
windows-1252编码有多个标签,例如"latin1
"、"iso-8859-1
"和"ascii
",这些标签在历史上对开发人员来说一直很困惑。在
Web 上以及任何希望通过实现本标准与 Web 兼容的软件中,这些标签是同义词:"latin1
"和"ascii
"只是windows-1252的标签,任何遵循本标准的软件,例如,在请求“Latin1”或“ASCII”解码该字节时,会将 0x80 解码为
U+20AC (€)。
不遵循本标准的软件并不总是给出相同的答案。这一问题的根源在于最初指定 Latin1 的文档(ISO/IEC 8859-1)未为字节范围 0x00 到 0x1F 或 0x7F 到 0x9F 提供任何映射。同样,最初指定 ASCII 的文档(ISO/IEC 646 等)未为字节范围 0x80 到 0xFF 提供任何映射。这意味着在请求使用 Latin1 或 ASCII 编码时,不同的软件为这些字节选择了不同的代码点映射。Web 浏览器和与浏览器兼容的软件选择根据windows-1252映射这些字节,windows-1252 是两者的超集,这一选择已在本标准中被规范化。其他软件会抛出错误,或使用同构解码或其他映射。[ISO8859-1][ISO646]
因此,实施者和开发人员在使用以“Latin1”或“ASCII”为术语的 API 的库时需要格外小心。如果这些库为原始规范中未定义的字节选择了其他行为,则它们很可能不会给出符合本标准的答案。
4.3. 输出编码
要从编码encoding获取输出编码,请执行以下步骤:
-
如果encoding是替代或UTF-16BE/LE,则返回UTF-8。
-
返回encoding。
获取输出编码算法对于 URL 解析和 HTML 表单提交非常有用,因为两者都需要完全符合此算法。
5. 索引
大多数旧的编码都使用索引。一个索引是一个有序的条目列表,每个条目由一个指针和一个对应的代码点组成。在一个索引中,指针是唯一的,而代码点可以重复。
一个高效的实现可能为每个编码提供两个索引。一个针对其解码器优化,另一个针对其编码器优化。
要在一个索引中找到指针及其对应的代码点,请执行以下步骤:令lines为将资源内容按 U+000A LF 分割的结果。然后移除lines中为空字符串或以 U+0023 (#) 开头的每一项。然后通过将lines中的每一项按 U+0009 TAB 分割来找到指针及其对应的代码点。第一个子项是指针(作为十进制数),第二个是对应的代码点(作为十六进制数)。其他子项不相关。
为了表示变化,一个索引包括一个标识符和一个日期。如果标识符发生变化,则索引也发生变化。
索引代码点是index中pointer对应的代码点,如果pointer不在index中,则为 null。
索引指针是index中codePoint对应的第一个指针,如果codePoint不在index中,则为 null。
除了GB18030 范围索引和ISO-2022-JP 片假名索引外,每个索引都有一个非规范性的可视化。JIS0208 索引也有一个替代的Shift_JIS可视化。此外,除了GB18030 范围索引和ISO-2022-JP 片假名索引外,每个索引的基本多语言平面覆盖范围也有可视化。
可视化的图例如下:
- 未映射
- UTF-8 中的两个字节
- UTF-8 中的两个字节,代码点紧接前一个指针的代码点
- UTF-8 中的三个字节(非 PUA)
- UTF-8 中的三个字节(非 PUA),代码点紧接前一个指针的代码点
- 专用使用
- 专用使用,代码点紧接前一个指针的代码点
- UTF-8 中的四个字节
- UTF-8 中的四个字节,代码点紧接前一个指针的代码点
- 重复代码点已在较早的索引中映射
- CJK 兼容表意文字
- CJK 统一表意文字扩展 A
以下是本规范定义的索引,不包括单字节索引,后者有自己的表格:
索引 | 备注 | |||
---|---|---|---|---|
Big5 索引 | index-big5.txt |