SyMind / learning

路漫漫其修远兮,吾将上下而求索。
10 stars 1 forks source link

Unicode 字符编码模型 #12

Open SyMind opened 3 years ago

SyMind commented 3 years ago

原文:http://www.unicode.org/reports/tr17/

概要

本文档阐明了一些用于描述字符编码的术语,以及 Unicode 的不同形式适用于哪些地方。它将互联网体系架构委员会(Internet Architecture Board,IAB)的三层文本流细化为四层结构。

状态

此文档已被 Unicode 成员和其他相关方审阅过,并已被 Unicode 联盟批准出版。这是一份稳定的文档,可以用作参考资料或被其他规范引用为规范性参考资料。

内容

  1. 字符编码模型
  2. 抽象字符表(Abstract Character Repertoire)
    1. 版本控制
    2. 字符与字形
    3. 兼容字符
    4. 子集
  3. 编码字符集(Coded Character Set,CCS)
  4. 字符编码方式(Character Encoding Form,CEF)
  5. 字符编码方案(Character Encoding Scheme,CES)
    1. 字节顺序
  6. Character Maps
  7. Transfer Encoding Syntax
  8. Data Types and API Binding
  9. Definitions and Acronyms
    1. References
    2. Acknowledgements
    3. Modifications

1 字符编码模型

Unicode 字符编码模型的四个层级可以概括为:

除了这四个独立的层级之外,还有两个实用的概念:

IAB 模型,被定义在 [RFC 2130] 中,分为三个层级:编码字符集(Coded Character Set, CCS),字符编码方案(Character Encoding Scheme,CES)和传输编码语法(Transfer Encoding Syntax,TES)。但是,为了充分应对 Unicode 字符编码模型的需要,定义了四个层级。其中,抽象字符表被隐式地定义在 IAB 模型中。Unicode 模型将 TES 独立于模型之外,同时在 CCS 和 CES 之间添加了一个额外的层级。

下面的部分给出了四个层级的定义、解释和示例,以及字符映射和传输编码语法。

2 抽象字符表

抽象字符表被定义为要编码的无序的抽象的字符的集合。抽象 的意思是这些对象是按照约定定义的。

字符表由两种类型: 封闭的(fixed)开放的(opened) 。在大多数字符编码中,封闭的字符表通常很小。字符表一旦确立,就永不变更。向指定的字符表添加一个新的抽象字符将创建一个新的字符表,然后给字符一个编号,构成一个新的对象。对于 Unicode 标准来说,字符表天生就是开放的。因为 Unicode 是通用编码,任何可被编码的抽象字符都是字符表的潜在成员,无论该字符当前是否已知。

字符表的例子:

2.1 版本控制

Unicode 标准通过发布主要版本和次要版本对字符表进行版本控制:1.0、1.1、2.0、2.1、3.0 等。每个版本的字符表由包含在该版本中的抽象字符枚举所定义。

Unicode 标准的字符表现在是严格按增量扩充的,尽管由于[Unicode]和[10646]的合并,在最早的版本(1.0 和 1.1)中没有完全保证增量扩充,影响了向后兼容性。从 2.0 版开始,Unicode 字符编码稳定性策略[Stability]保证了不会从字符表中移除任何字符。

注意:Unicode 字符编码稳定性策略也以其他形式限制对标准的变更。例如,许多字符属性受到一致性的约束,一些属性一旦被分配就不能变更。对规范化稳定性的保证防止了变更会破坏现有编码字符的映射,也限制了未来的版本中可以被添加到字符表中的字符类型。

2.2 字符与字形

字符表的元素是抽象字符。字符不同于字形,字形是代表一个字符或字符一部分的特殊图像。相同字符的字形可能有非常不同的形状,如下图所示的字母 a。

字符 字形
image image image image image image image

字形并不总是对应一个字符。例如,序列“f”后跟“ i”可以显示为单个字形,称为“连字”。请注意,字形已合并在一起,点从“ i”中消失,如下图所示。

字符序列 字形
image image !image

另一方面,可以通过两个字形序列来实现 fi,如下图中所示的假想示例。是使用单个字形还是使用两个字形的序列取决于字体和渲染软件。

字符序列 字形
image image image image

2.4 子集

与大多数字符表不同,Unicode 覆盖范围是所有字符。考虑到编写系统时的复杂性,在实际中这意味着几乎所有的实现都只是支持字符表的某个子集,而非所有字符。

Unicode 标准没有预设子集,每个实现都需要定义并支持它希望解析的字符表的子集。

3 编号字符集

编码字符集被定义为一个从抽象字符集到非负整数集的映射。整数范围不必是连续的。在 Unicode 标准中,Unicode 标量值显式定义了这样一个不连续的整数范围。

如果编码字符集将一个抽象字符映射一个整数,则视为它被定义在了一个编码字符集中。该整数是指定抽象字符的 码点(code point ) 。然后,这个抽象字符就是一个编码字符。

编码字符集是 ISO 和字符编码委员会产出的基本对象。它们将已定义的字符表与非负整数关联起来,然后可以明确地使用非负整数来引用字符表中的特定抽象字符。

编码字符集也可以称为字符编码(character encoding)、编码字符表(coded character repertoire)、字符集定义(character set definition)或代码页(code page)。

3.1 字符命名

负责字符编码的 JTC1 小组委员会 SC2 要求为其编码字符集中的每个抽象字符分配唯一的字符名。供应商编码的字符集或 SC2 以外的标准委员会产出的编码通常不遵循这种做法,在这些编码中,为字符提供的名称通常都是变量和注释,而不是字符编码的标准部分。

3.2 代码空间

被用于映射抽象字符的非负整数范围定义代码空间的相关概念。代码空间类型的传统边界与编码方式密切相关(见下文),因为抽象字符到非负整数的映射是在特定的编码方式下完成的。有效代码空间的例子有 0..7F、0..FF、0..FFFF、0..10FFFF、0..7FFFFFFF、0..FFFFFFFF。

4 字符编码方式

字符编码方式是从 CCS 中的整数集到 码元(code unit ) 序列集的映射。码元是一个整数,表示在计算机体系结构中占用特定二进制的宽度,例如 8 位字节。编码方式将字符表示为计算机中的实际数据。码元序列不一定具有相同的长度。

编码字符集的字符编码方式被定义为映射该编码字符集中所有编码字符的字符编码方式。

注意:在许多情况下,对于给定的编码字符集只有一种字符编码方式。在某些情况下,只指定了字符编码方式。依赖码元序列和整数之间的隐式关系,隐式定义了编码字符集。

解析一个字符序列时,由三种可能性:

  1. 序列格式错误(ill-formed)。 序列不完整,或者无法匹配编码方式规范。例如
    • 在 CP950 中 0xA3 不完整。若其后面没有跟随另一个格式正确的字节,那么格式错误。
    • 在UTF-16 中 0xD800 不完整。若其后面没有跟随另一个格式正确的 16 位值,那么格式错误。
    • 在 UTF-8 中 0xC0 格式错误。它不是一个格式正确的 UTF-8 序列的初始字节。
  2. 序列表示一个有效码点,但是没有被分配。这个序列可能在将来的某个字符编码的版本中被分配。
    • 在1999年之前,在 CP950 中 0xA3 0xBF 未被分配。
    • 在 Unicode 5.0 中 0x0EDE 未被分配。
  3. 源序列被分配:它表示一个有效的编码字符。它有三种类型: 第一种类型,是一个普通的被分配的字符,例如
    • 在 Unicode 5.0 中 0x0EDD 被分配 第二种类型,是一个需用户自行定义的(user-defined)字符,例如
    • 0xE000 是一个需要用户进行定义的字符,它的语义解释由标准以外的各方达成一致。 第三种类型,是 Unicode 标准特有的:非字符(noncharacter)。这是一种内部使用的需用户自行定义的字符,不公开使用。例如
    • 在 Unicode 5.0 种 0xFFFF 是一个被分配的非字符

CCS 的编码方式可以产生与抽象字符相关的固定宽度或可变宽度的码元序列。编码方式可能涉及 CCS 的整数到一组码元序列的任意可逆映射。

编码方式有多种类型。下面是一些有关编码方式的更重要的例子。

定宽编码方式的例子:

名字 字符被编码为 备注
7-bit a single 7-bit quantity example: ISO 646
8-bit G0/G1 a single 8-bit quantity with constraints on use of C0 and C1 spaces
8-bit a single 8-bit quantity with no constraints on use of C1 space
8-bit EBCDIC a single 8-bit quantity with the EBCDIC conventions rather than ASCII conventions
16-bit (UCS-2) a single 16-bit quantity within a code space of 0..FFFF
32-bit (UCS-4) a single 32-bit quantity within a code space 0..7FFFFFFF
32-bit (UTF-32) a single 32-bit quantity within a code space of 0..10FFFF
16-bit DBCS process code a single 16-bit quantity example: UNIX widechar implementations of Asian CCS's
32-bit DBCS process code a single 32-bit quantity example: UNIX widechar implementations of Asian CCS's
DBCS Host two 8-bit quantities following IBM host conventions

变宽编码方式的例子:

名字 字符被编码为 备注
UTF-8 Unicode 中1到4个8位码元 只被用于 Unicode
UTF-16 1到2个16位码元 只被用于 Unicode

编码方式定义了编码的一个基本问题:每个字符有多少码元。每个字符的码元数量对国际化软件很重要。在以前,这等同于每个字符需要多少字节表达。随着 Unicode 和 10646 引入 UCS-2、UTF-16、UCS-4 和 UTF-32,它泛化为两个概念:码元的规范宽度,和用于表示每个字符所需的码元数量。与 ISO/IEC 10646 相关联的 UCS-2 编码方式,仅能表示 BMP 中的字符,是一种定宽编码方式。相比之下,UTF-16 使用一个或两个码元,能够覆盖整个 Unicode 的代码空间。

UTF-8 是一个很好的例子。在 UTF-8 中,用于表示字符数据的基本码元是 8 位宽(即一个字节或八位)。UTF-8 的宽度映射为:

0x00..0x7F 1 byte
0x80..0x7FF 2 bytes
0x800..0xD7FF, 0xE000..0xFFFF 3 bytes
0x10000 .. 0x10FFFF 4 bytes

用于特定编码字符集的编码方式示例:

名字 编码方式
JIS X 0208 generally transformed from the kuten notation to a 16-bit “JIS code” encoding form, for example "nichi", 38 92 (kuten) → 0x467C JIS code
ISO 8859-1 has the 8-bit G0/G1 encoding form
CP 037 8-bit EBCDIC encoding form
CP 500 8-bit EBCDIC encoding form
US ASCII 7-bit encoding form
ISO 646 7-bit encoding form
Windows CP 1252 8-bit encoding form
Unicode 4.0, 5.0 UTF-16, UTF-8, or UTF-32 encoding form
Unicode 3.0 either UTF-16 (default) or UTF-8 encoding form
Unicode 1.1 either UCS-2 (default) or UTF-8 encoding form
ISO/IEC 10646:2003 depending on the declared implementation levels, may have UCS-2, UCS-4, UTF-16, or UTF-8.
ISO/IEC 10646:2009 UTF-8, UTF-16, or UTF-32

用于特定编码字符集的编码方式示例:

名字 编码方式
JIS X 0208 generally transformed from the kuten notation to a 16-bit “JIS code” encoding form, for example "nichi", 38 92 (kuten) → 0x467C JIS code
ISO 8859-1 has the 8-bit G0/G1 encoding form
CP 037 8-bit EBCDIC encoding form
CP 500 8-bit EBCDIC encoding form
US ASCII 7-bit encoding form
ISO 646 7-bit encoding form
Windows CP 1252 8-bit encoding form
Unicode 4.0, 5.0 UTF-16, UTF-8, or UTF-32 encoding form
Unicode 3.0 either UTF-16 (default) or UTF-8 encoding form
Unicode 1.1 either UCS-2 (default) or UTF-8 encoding form
ISO/IEC 10646:2003 depending on the declared implementation levels, may have UCS-2, UCS-4, UTF-16, or UTF-8
ISO/IEC 10646:2009 UTF-8, UTF-16, or UTF-32

5 字符编码方案

字符编码方案(CES)是从码元序列到字节序列的可逆转换,用以下三种方法中的一种:

  1. 简单 CES 按顺序将 CEF 的每个码元映射到唯一的序列化的字节序列。
  2. 复合 CES 使用两种或更多简单的 CES,加上它们之间的转换方法。 复合 CES 的性质意味着可能有不同的字节序列对应于相同的码元序列。虽然这些序列不是唯一的,但是码元的原始序列可以从这些序列中毫不含糊地恢复。
  3. 压缩 CES 将码元序列映射到字节序列,同时最小化字节序列的长度。一些压缩 CES 被设计成为每个码元序列产生一个唯一的字节序列,以便可以比较压缩的字节序列是否相等或通过二进制比较进行排序。其他压缩 CES 仅仅是可逆的。

字符编码方案与跨平台持久化数据的问题相关,例如码元比字节宽,在这种情况下,可能需要进行字节交换以将数据匹配指定平台的字节顺序。特别是

重要的是不要混淆字符编码方式(CEF)和 CES。

  1. CEF 将码点映射到码元,而 CES 将码元序列转换为字节序列。
  2. CES 必须考虑 CEF 中所有比字节宽的码元的序列化字节顺序。

一些 Unicode 编码方案与三种 Unicode 编码方式的命名相同。在不加限定地使用时,术语 UTF-8、UTF-16 和 UTF-32 是作为Unicode 编码方式还是作为 Unicode 编码方案并不明确。这种不明确对 UTF-8 通常是无危险的,因为 UTF-8 编码方案很容易从 UTF-8 编码方式定义的字节序列派生出来。然而,对于 UTF-16 和 UTF-32,这种不明确就有问题了。作为编码方式,UTF-16 和 UTF-32 的码元通过 16 位或 32 位的数据类型访问内存;它没有相关的字节顺序,也没有使用 BOM。

作为编码方案,UTF-16 和 UTF-32 表示序列化字节,例如数据流或文件中的序列化字节;它们的字节顺序是两种中的一种,并且数据的首部可能有一个 BOM。当 UTF-16 或 UTF-32 的使用可能会被误解,并且它们作为 Unicode 编码方式或 Unicode 编码方案的使用之间的区别很重要时,应该使用完整的术语。例如,使用 UTF-16 编码方式或 UTF-16 编码方案。它们也可以分别缩写为 UTF-16 CEF 或 UTF-16 CES。

Unicode 字符编码方案的例子:

压缩字符编码方案的例子:

字节顺序

处理器架构对多字节机器整数映射到存储位置的处理方式不同。小端架构将最低有效字节放在较低的地址,而大端架构从最高有效字节开始。

操作内存中的码元时,这种差异无关紧要,但是当使用指定的 CES 将码元序列化为字节序列时,字节顺序就变得重要了。在读取数据流时,有两种字节顺序:与处理器读取的字节顺序相同或相反。在前一种情况下,不需要采取特别的操作;在后一种情况下,数据在处理之前需要进行字节反转。

根据数据流的外部标记,区分三种字节顺序:Big Endian(BE)、Little Endian(LE)和 默认或内部标记(default or internally marked)

在 Unicode 中,码点为 U+FEFF 的字符被定义为字节顺序标记,而它的字节反转后对应的码点 U+FFFE 是 UTF-16 中的非字符(U+FFFE)。因此,在数据流首部,字节顺序标记用于明确地表示码元的字节顺序。