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 个平面。

UTF-32 与 UCS-4
在 Unicode 与 ISO 10646 合并之前,ISO 10646 标准为“通用字符集”(UCS)定义了一种 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 0xxxxxxx
2 000080-0007FF 110xxxxx 10xxxxxx
3 000800-00FFFF 1110xxxx 10xxxxxx 10xxxxxx
4 010000-10FFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

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
是否依赖字节序


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