Unicode、UCS、BMP、UTF-8、UTF-16、UTF-32

Unicode是一种字符编码方法,不过它是由国际组织设计,可以容纳全世界所有语言文字的编码方案。Unicode的学名是"Universal Multiple-Octet Coded Character Set",简称为UCS。UCS可以看作是"UnicodeCharacter Set"的缩写。

UCS有两种格式:UCS-2和UCS-4。顾名思义,UCS-2就是用两个字节编码,UCS-4就是用4个字节(实际上只用了31位,最高位必须为0)编码。  UCS-2有2^16=65536个码位,当时是没有问的,经过一段时间,不可避免的事情发生了,unicode字符超过了65536个,主要原因是加入了大量汉语、日语、韩语等表意文字,这里加入的是不常用的汉字,常用的汉字早已加入进去。

字符集

字数

Unicode 编码

基本汉字

20902

4E00-9FA5

基本汉字补充

74

9FA6-9FEF

扩展A

6582

3400-4DB5

扩展B

42711

20000-2A6D6

扩展C

4149

2A700-2B734

扩展D

222

2B740-2B81D

扩展E

5762

2B820-2CEA1

扩展F

7473

2CEB0-2EBE0

康熙部首

214

2F00-2FD5

部首扩展

115

2E80-2EF3

兼容汉字

477

F900-FAD9

兼容扩展

542

2F800-2FA1D

PUA(GBK)部件

81

E815-E86F

部件扩展

452

E400-E5E8

PUA增补

207

E600-E6CF

汉字笔画

36

31C0-31E3

汉字结构

12

2FF0-2FFB

汉语注音

43

3105-312F

注音扩展

22

31A0-31BA

1

3007


这张表中蓝色的为最后补加的不常用字符。我们在程序中经常要判断是否有中文,字符编码如果落在[\u4e00-\u9fa5]区间就认为有中文,其他情况就不管了。


UCS-4 理论上有2^31=2147483648个码位,但是实际上没有那么多,因为要兼容ucs-2,还要尽量节省空间。

UCS-4 实际编码空间从U+0000到+10FFFF,共有1,112,064个码位(codepoint)可用来映射字符,这个数量满足需求。Unicode的编码空间可以划分为17个平面(plane),每个平面包含65,536个码位。17个平面的码位可表示为从U+xx0000到U+xxFFFF,其中xx表示从0x00到0x10,共计17个平面。第一个平面称为基本多语言平面(Basic Multilingual Plane, BMP),或称第零平面(Plane 0),或者说UCS-4中,高两个字节为0的码位被称作BMP。

将UCS-4的BMP去掉前面的两个零字节就得到了UCS-2。在UCS-2的两个字节前加上两个零字节,就得到了UCS-4的BMP。其他平面称为辅助平面(Supplementary Planes),辅助平面有16个,共包含16*65536=2^20个码位。基本多语言平面内还有一块保留区,从U+D800到U+DFFF,将这个区分成两块U+D800~U+DBFF用于第一个代码单元,U+DC00~U+DFFF用于第二个代码单元,这两个代码单元可表于的码位2^10*2^10=2^20,正好满足要求。

UCS只是规定如何编码,并没有规定如何传输、保存这些编码

规定存储方式的称为UTF(Unicode Transformation Format),其中就有UTF-8、UTF-16和UTF-32。

UTF-16

UTF-16由RFC2781规定,它使用两个字节来表示一个代码点。

不难猜到,UTF-16是完全对应于UCS-2的,即把UCS-2规定的代码点通过Big Endian(大端)或LittleEndian(小端)方式直接保存下来。UTF-16包括三种:UTF-16,UTF-16BE(Big Endian),UTF-16LE(LittleEndian)。

其实BOM是个小聪明的想法。只要出现 FEFF 或 FFFE就可以判断出是Big Endian还是Little Endian。如果没有BOM编译器会智能的分析,从而找出是Big Endian还是Little Endian。

举个例子。“ABC”这三个字符用各种方式编码后的结果如下:

UTF-16

00 41 00 42 00 43

UTF-16 BE

00 41 00 42 00 43

UTF-16 LE

41 00 42 00 43 00

UTF-16 BE BOM

FE FF 00 41 00 42 00 43

UTF-16 LE BOM

FF FE 41 00 42 00 43 00

大端和小端的区别就是一个字的两个字节对掉。

另外,UTF-16还能表示一部分的UCS-4代码点——U+10000~U+10FFFF。表示算法比较复杂,简单说明如下:

  1. 从代码点U中减去0x10000,得到U'。这样U+10000~U+10FFFF就变成了 0x00000~0xFFFFF。 
  2. 用20位二进制数表示U'。 U'=yyyyyyyyyyxxxxxxxxxx 
  3. 将前10位和后10位用W1和W2表示,W1=110110yyyyyyyyyy,W2=110111xxxxxxxxxx,则 W1 = D800~DBFF,W2 = DC00~DFFF。

例如,U+12345表示为 D8 08 DF 45(UTF-16BE),或者08 D8 45 DF(UTF-16LE)。

但是由于这种算法的存在,造成UCS-2中的 U+D800~U+DFFF 变成了无定义的字符。


UTF-32

UTF-32用四个字节表示代码点,这样就可以完全表示UCS-4的所有代码点,而无需像UTF-16那样使用复杂的算法。与UTF-16类似,UTF-32也包括UTF-32、UTF-32BE、UTF-32LE三种编码,UTF-32也同样需要BOM字符。仅用'ABC'举例:

UTF-32BE

00 00 00 41 00 00 00 42 00 00 00 43

UTF-32LE

41 00 00 00 42 00 00 00 43 00 00 00

UTF-32(Big Endian)

00 00 FE FF 00 00 00 41 00 00 00 42 00 00 00 43

UTF-32(Little Endian)

FF FE 00 00 41 00 00 00 42 00 00 00 43 00 00 00

UTF-32(不带BOM)

00 00 00 41 00 00 00 42 00 00 00 43


UTF-8

UTF-16UTF-32的一个缺点就是它们固定使用两个或四个字节,这样在表示纯ASCII文件时会有很多00字节,造成浪费。而RFC3629定义的UTF-8则解决了这个问题。

UTF-8来说,每一个字节的前两位尤为重要,按照前两位的不同,一共有四种排列组合:00xxxxxx01xxxxxx10xxxxxx11xxxxxx
1所有以0开始的字节,都与原来的ASCII码兼容,也就是说,0xxxxxxx不需要额外转换,就是我们平时用的ASCII码。 
2所有以10开始的字节,都不是每个UNICODE的第一个字节,都是紧跟着前一位。例如:10110101,这个字节不可以单独解析,必须通过前一个字节来解析,如果前一个也是10开头,就继续前嗍。 
3所有以11开始的字节,都表示是UNICODE的第一个字节,而且后面紧跟着若干个以10开头的字节。如果是110xxxxx(就是最左边的0的左边有21),代表后面还有110xxxxxx;如果是1110xxxx(就是最左边的0的左边有31),代表后面还有210xxxxxx;以此类推,一直到1111110x 

具体的表格如下: 
1
字节 0xxxxxxx 
2
字节 110xxxxx 10xxxxxx 
3
字节 1110xxxx 10xxxxxx10xxxxxx 
4
字节 11110xxx 10xxxxxx 10xxxxxx10xxxxxx

UCS-2 (UCS-4)

位序列

第一字节

第二字节

第三字节

第四字节

U+0000 ~ U+007F

00000000-0xxxxxxx

0xxxxxxx

U+0080 ~ U+07FF

00000xxx-xxyyyyyy

110xxxxx

10yyyyyy

U+0800 ~ U+FFFF

xxxxyyyy-yyzzzzzz

1110xxxx

10yyyyyy

10zzzzzz

U+10000~U+1FFFFF

00000000-000wwwxx-xxxxyyyy-yyzzzzzzz

11110www

10xxxxxx

10yyyyyy

10zzzzzz


可见,ASCII字符(U+0000~U+007F)部分完全使用一个字节,避免了存储空间的浪费。另外,从上表中可以看出,单字节编码的第一字节为[00-7F],双字节编码的第一字节为[C2-DF],三字节编码的第一字节为[E0-EF]。这样只要看到第一个字节的范围就可以知道编码的字节数。这样也可以大大简化算法。


猜你喜欢

转载自blog.csdn.net/hong10086/article/details/80654380