#### Unicode 编码规则

ANSI，American National Standards Institute 美国国家标准学会，由这个标准学会制订的一种编码规则，也叫 MBCS（Muilti-Bytes Charecter Set，多字节字符集）。

在最初的时候，Internet 上只有一种字符集—— ANSI 的 ASCII 字符集，它使用 7 bits 来表示一个 字符，总共表示 128 个字符，其中包括了 英文字母、数字、标点符号等常用字符。 之后，又进行扩展，使用 8 bits 表示一个字符，可以表示 256 个字符，主要在原来的 7 bits 字符集的基础上加入了一些特殊符号。 后来，由于各国语言的加入，ASCII 已经不能满足信息交流的需要，为了能够表示其它国家的文字，各国在 ASCII 的基础上制定了自己的字符集，这些从 ANSI 标准派生的字符集被习惯的统称为 ANSI 字符集，它们正式的名称应该是 MBCS(Multi-Byte Chactacter System，即多字节字符系统)。 这些派生字符集的特点是以 ASCII 127 bits 为基础，兼容 ASCII 127，他们使用大于 128 的编码作为一个 Leading Byte，紧跟在 Leading Byte 后的第二（甚至第三）个字符与 Leading Byte 一起作为实际的编码。 这样的字符集有很多，我们常见的 GB-2312 就是其中之一。

不同 ANSI 编码之间互不兼容，当信息在国际间交流时，无法将属于两种语言的文字，存储在同一段 ANSI 编码的文本中。 一个很大的缺点是：同一个编码值，在不同的编码体系里代表着不同的字，这样就容易造成混乱，导致了 Unicode 码 的诞生。 为了统一所有文字的编码，Unicode 应运而生。Unicode 把所有语言都统一到一套编码里，这样就不会再有乱码问题了。

字符集： 为每一个 `字符` 分配一个唯一的数字 ID（学名为码位/码点/Code Point）。
编码规则： 将 `码位` 转换为 `字节序列` 的规则。

Unicode 编码点分为 17 个平面（plane），每个平面包含 2^16（即65536）个码位（Code Point）。 17 个平面的码位可表示为从 U+xx0000 到 U+xxFFFF，其中 xx 表示十六进制值从 00 到 10 ，共计 17 个平面。
17 * 65536 = 1114112，所以 Unicode 能表示 1114112 个不同的字符，每个字符都有对应的唯一数字。

编码规则中还有另外一个重要的概念，编码单元（Code Unit），指编码一个字符需要的最少字节数， 比如 UTF-8 需要最少一个字节，UTF-16 最少两个字节，UCS-2 两个字节，UCS-4 和 UTF-32 四个字节。 UTF-8 和 UTF-16 是变长编码，UCS-2、UCS-4 和 UTF-32 是定长编码。

UTF-32 与 UCS-4
在 Unicode 与 ISO 10646 合并之前，ISO 10646 标准为“通用字符集”（UCS：Unicode Character Set）定义了一种 31 位的编码形式（即 UCS-4），其编码固定占用 4 个字节，编码空间为 0x00000000~0x7FFFFFFF（可以编码 20 多亿个字符）。 UCS-4 有 20 多亿个编码空间，但实际使用范围并不超过 0x10FFFF，并且为了兼容 Unicode 标准，ISO 也承诺将不会为超出 0x10FFFF 的 UCS-4 编码赋值。由此 UTF-32 编码被提出来了，它的编码值与 UCS-4 相同，只不过其编码空间被限定在了 0~0x10FFFF 之间。因此也可以说：UTF-32 是 UCS-4 的一个子集。

UTF-16 与 UCS-2
除了 UCS-4，ISO 10646 标准为“通用字符集”（UCS）定义了一种 16 位的编码形式（即 UCS-2），其编码固定占用 2 个字节，它包含 65536 个编码空间（可以为全世界最常用的 63K 字符编码，为了兼容 Unicode，0xD800-0xDFFF 之间的码位未使用）。例：“汉”的 UCS-2 编码为 6C49。 但两个字节并不足以真正地“一统江湖”（a fixed-width 2-byte encoding could not encode enough characters to be truly universal），于是 UTF-16 诞生了，与 UCS-2 一样，它使用两个字节为全世界最常用的 63K 字符编码，不同的是，它使用 4 个字节对不常用的字符进行编码。UTF-16 属于变长编码。
前面提到过：Unicode 编码点分为 17 个平面（plane），每个平面包含 2^16（即 65536）个码位（code point），而第一个平面称为“基本多语言平面”（Basic Multilingual Plane，简称 BMP），其余平面称为“辅助平面”（Supplementary Planes）。其中“基本多语言平面”（0~0xFFFF）中 0xD800~0xDFFF 之间的码位作为保留，未使用。UCS-2 只能编码“基本多语言平面”中的字符，此时 UTF-16 与 UCS-2 的编码一样（都直接使用 Unicode 的码位作为编码值），例：“汉”在 Unicode 中的码位为 6C49，而在 UTF-16 编码也为 6C49。另外，UTF-16 还可以利用保留下来的 0xD800-0xDFFF 区段的码位来对“辅助平面”的字符的码位进行编码，因此 UTF-16 可以为 Unicode 中所有的字符编码。

UTF-16 中如何对“辅助平面”进行编码呢？
Unicode 的码位区间为 0~0x10FFFF，除“基本多语言平面”外，还剩 0xFFFFF 个码位（并且其值都大于或等于 0x10000）。对于“辅助平面”内的字符来说，如果用它们在 Unicode 中码位值减去 0x10000，则可以得到一个 0~0xFFFFF 的区间（该区间中的任意值都可以用一个 20-bits 的数字表示）。该数字的前 10 位(bits)加上 0xD800，就得到 UTF-16 四字节编码中的前两个字节；该数字的后 10 位(bits)加上 0xDC00，就得到 UTF-16 四字节编码中的后两个字节。例如： “𪺫” 这个汉字的 Unicode 码位值为 2AEAB，减去 0x10000 得到 1AEAB（二进制值为 0001 1010 1110 1010 1011），前 10 位加上 D800 得到 D86B，后 10 位加上 DC00 得到 DEAB。于是该字的 UTF-16 编码值为 D86BDEAB（该值为大端表示，小端为 6BD8ABDE）

使用 C# 输出汉字“𪺫”：

    string hex = "6BD8ABDE";
    byte[] hexByteArr = Enumerable.Range(0, hex.Length)
                                        .Where(x => x % 2 == 0)
                                        .Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
                                        .ToArray();
    string word = UnicodeEncoding.Unicode.GetString(hexByteArr);

UTF-8
从前述内容可以看出：无论是 UTF-16/32 还是 UCS-2/4，一个字符都需要多个字节来编码，这对那些英语国家来说多浪费带宽啊！（尤其在网速本来就不快的那个年代。。。）由此，UTF-8 产生了。在 UTF-8 编码中，ASCII 码中的字符还是 ASCII 码的值，只需要一个字节表示，其余的字符需要 2 字节、3 字节或 4 字节来表示。

UTF-8 的编码规则：
(1) 对于 ASCII 码中的符号，使用单字节编码，其编码值与 ASCII 值相同。其中 ASCII 值的范围为 0~0x7F，所有编码的二进制值中第一位为 0（这个正好可以用来区分单字节编码和多字节编码）。
(2) 其它字符用多个字节来编码（假设用 N 个字节），多字节编码需满足：第一个字节的前 N 位都为 1，第 N+1 位为 0，后面 N-1 个字节的前两位都为 10，这 N 个字节中其余位全部用来存储 Unicode 中的码位值。

字节数 | Unicode | 十进制 | UTF-8 编码
--- | --- | --- | ---
1 | 000000-00007F | 0-127 | 0xxxxxxx
2 | 000080-0007FF | 128-2047 | 110xxxxx 10xxxxxx
3 | 000800-00FFFF | 2048-65535 | 1110xxxx 10xxxxxx 10xxxxxx
4 | 010000-10FFFF | 65536-1114111 | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

从上面表格可以看出，大于 ASCII 码的，就会由上面的第一位元组的前几位表示该 Unicode 字元的长度， 比如 110xxxxx 前三位的二进位表示告诉我们这是个 2 Byte 的 Unicode 字元； 1110xxxx 是个三位的 Unicode 字元，依此类推； xxxxx 的位置由字符编码数的二进制表示的位填入，注意在多字节串中，第一个字节的开头 "1" 的数目就是整个串中字节的数目。

UTF 是英文 Unicode Transformation Format 的缩写，可以翻译成 Unicode 字符集转换格式，即怎样将 Unicode 定义的数字转换成程序数据 。
UTF 系列编码方案（UTF-8、UTF-16、UTF-32）均是由 Unicode 编码方案衍变而来，以适应不同的数据存储或传递，它们都可以完全表示 Unicode 标准中的所有字符。 目前，这些衍变方案中 UTF-8 和 UTF-16 被广泛使用，而 UTF-32 则很少被使用。

UTF-8、UTF-16、UTF-32、UCS-2、UCS-4 对比

对比 | UTF-8 | UTF-16 | UTF-32 | UCS-2 | UCS-4
--- | --- | --- | --- | --- | ---
编码空间 | 0-10FFFF | 0-10FFFF | 0-10FFFF | 0-FFFF | 0-7FFFFFFF
最少编码字节数 | 1 | 2 | 4 | 2 | 4
最多编码字节数 | 4 | 4 | 4 | 2 | 4
是否依赖字节序 | 否 | 是 | 是 | 是 | 是

字节序 BOM(Byte Order Mark)
Unicode 的 BOM

Encoding | BOM | Endian | 十进制 | 十进制 | byte[]
--- | --- | --- | --- | --- | ---
UTF-7 | 2B 2F 76 38 2D | endianness | 185479870509 | 43,47,118,56,45 | 43,47,118,56,45
UTF-8 | EF BB BF | endianness | 15711167 | 239,187,191 | -17,-69,-65
UTF-16-LE | FF FE | little endian | 65534 | 255,254 | -1,-2
UTF-16-BE | FE FF | big endian | 65279 | 254,255 | -2,-1
UTF-32-LE | FF FE 00 00 | little endian | 4294836224 | 255,254,0,0 | -1,-2,0,0
UTF-32-BE | 00 00 FE FF | big endian | 65279 | 0,0,254,255 | 0,0,-2,-1

UTF-8 以字节为编码单元，没有字节序的问题，不需要 BOM 来表明字节顺序，但可以用 BOM 来表明编码方式。
字符"ZERO WIDTH NO-BREAK SPACE"的 UTF-8 编码是 EF BB BF。

更多字节序的问题，请参考 维基百科 字节序

Unicode 误区

- Unicode 只不过是 16 比特编码。一些人误认为 Unicode 只不过是 16 比特编码，每个字符占用 16 比特，因此一共有 65,536 个可能的字符。实际上这是不正确的。这样是关于 Unicode 的最大误解，所以也难怪一些人会这么想。
- 任何未分配的代码点都可以用于内部用途？错。最终，那些未分配的地方都会被某个字符使用。你应该使用私有用途代码点，或非字符代码点。
- 每个 Unicode 代码点都表示一个字符？错。有许多非字符代码点（FFFE，FFFF，1FFFE，……），还有许多代理代码点、私有代码点和未分配的代码点，还有控制和格式字符（RLM，ZWNJ，……）
- 字符映射是一对一的？错。映射关系也可能是：
  - 一对多：(ß → SS )
  - 上下文相关：(…Σ ↔ …ς 和 …ΣΤ… ↔ …στ… )
  - 语言相关：( I ↔ ı 和 İ ↔ i )

#### 0 条用户评论

0 / 300
当前仅支持登录用户评论，去登录
© 2021 - 2025 必搜便捷工具 All rights reserved.
问题反馈 皖ICP备2021019041号 English