chenqunfeng / Blog

个人技术记录博客
6 stars 1 forks source link

字符编码 #6

Open chenqunfeng opened 8 years ago

chenqunfeng commented 8 years ago

前言

我们在计算机屏幕上的文字,不管是中文也好,英文也罢,甚至是其他国家的语言文字,它们在计算机中的存储实际上是二进制的比特流。既然是二进制的比特流,那么从二进制比特流到能正确显示文字,则在这两者之间需要一个转换规则。

介绍

字集码是把字符集中的字符编码为指定集合中某一对象,以便文本在计算机中存储和通过通信网络的传递。而对于一个字符集,若要将一个编码进行正确的转码,需要三个关键元素:字库表(character repertoire)、编码字符集(coded character set)、字符编码(character encoding form)。其中字库表决定整个字符集所有字符的范围;编码字符集是用一个编码(code point)来表示一个字符在字库中的位置;字符编码则表示编码字符集和实际存储值之间的转换关系,不过一般都会直接将编码(code point)直接存储。

字符集

Unicode

中文为万国码、国际码、统一码、单一码(统一码联盟制定),是计算机科学领域里的一项业界标准。它对世界上大部分的文字系统进行整理、编码,使得电脑可以用更为简单的方式来呈现和处理文字。 Unicode是国际组织指定的可以容纳世界上所有文字和符号的字符编码方案。目前的Unicode字符分为17组编排,0x0000到0x10FFFF(00,01,02,03...0F,10),每组称为平面,每平面拥有65536个码位,共1114112个。 通用字符集(UCS)是由ISO 10646(或称ISO/IEC 10646)标准所定义的标准字符集。UCS-2用两个字节编码,UCS-4是一个更大的尚未填充完全的31位字符集,加上恒为0的首位,共32位,用四个字节编码(实际上最大码位为0x7FFFFFFF,共有2的31次方为20亿个)。 统一码联盟和ISO分别制定了Unicode和UCS,但事实上两者都在做同样的事情,所以双方进行了整合。直到Unicode2.0时,Unicode的编码和UCS的编码基本一致。 虽然Unicode在编码上和UCS保持一致,但是在实现上有自己的规则,而UCS只定义了编码标准。Unicode的实现方式有UTF-8,UTF-16,UTF-32还有UTF-7等。 其中UTF-16是UCS-2的扩展,UTF-32是UCS-4的子集。

编码方案

UTF-8

UTF-8(8-bit Unicode Transformation Format)是一种针对Unicode的可变长字符编码。现在标准化为RFC 3629,使用1到4个字节编码Unicode字符。

编码方式(算法)

对与UTF-8中的任意字节B

例子

00000000 00000000 0xxxxxxx => 0xxxxxxx(ASCII码范围) 00000000 00000xxx yyyyyyyy => 110xxxyy 10yyyyyy(第一个字节由110开始,接着的字节由10开始) 00000xxx yyyyyyyy zzzzzzzz => 11110xxx 10yyyyyy 10yyzzzz 10zzzz00(第一个字节由11110开始,接着的字节由10开始) ......

优点:

  1. ASCII码是UTF-8的一个子集。因为纯ASCII字符串也是一个合法的UTF-8字符串,所以现存的ASCII文本不需要转换。
  2. UTF-8的编码方式(算法)可以简单识别一个UTF-8字符串,也是就说一个字符串在其他编码中表现为合法的UTF-8的可能性很低,几乎不用担心。

缺点:

  1. 不利于正则表达式检索。正则表达式可以进行很多英文高级的模糊检索,例如,[a-h]表示a到h间所有字母。同样GBK编码的中文也可以这样利用正则表达式,比如在只知道一个字的读音而不知道怎么写的情况下,也可用正则表达式检索,因为GBK是按读音排序的。但是UTF-8不是按读音排序的,所以会对正则表达式检索造成不利影响。不过Unicode是按部首排序的,因此在只知道一个字的部首而不知道如何发音的情况下,UTF-8可用正则表达式检索而GBK不行。
  2. 虽然UTF-8中ASCII码占用的空间只有一半,可是在一些字符的UTF-8编码占用的空间就要多出1/3,特别是中文、日文和韩文这样的方块文字。

    UTF-16

    UTF-16是Unicode字符编码五层次模型的第三层:字符编码表的一种实现方式。即把Unicode字符集的抽象码位映射为16位长的整数序列,用于数据存储或传递,是Unicode的一种变长表示(UTF-16使用16位长的整数表示,而Unicode需要1个或者2个16位长的码元来表示 )。

    编码方式(算法)

    辅助平面(Supplementary Planes)中的码位,在UTF-16中被编码为一对(两个)16比特长的码元(即32bit,4Bytes),称作代理对,具体方法如下:

例子

如U+10437

UTF-16大尾序表示为(UTF-16BE):0xD801 0xDC37 UTF-16小尾序表示为(UTF-16LE):0x01D8 0x37DC UTF-16大小尾的区分方式: 在UTF-16文件的开首,都会放置一个U+FEFF字符作为Byte Order Mark,其中U+FFFE为UTF-16LE,U+FEFF为UTF-16RE。

UTF-32

UTF-32(或UCS-4)是一种将Unicode字符编码的协定,对每一个Unicode码位使用恰好32位。因为UTF-32对每个字符都使用4字节,虽然固定字节在使用上会比较方便,但是却是其他编码占用大小的二到四倍,是非常没有效率的。 UTF-32是UCS-4的子集。UCS-4用来表示所有Unicode的字码空间,最大码位为0x7FFFFFFF,共有20亿个,但是如此大的字码空间实际上却只利用了很小的字码集,所以更小的码集被提出,也就是UTF-32,使用32位,范围在0x000000到0x10FFFF,共1114112个。

小扩展:UTF中的BOM

例子:

假设"甲"的Unicode编码为594E,"乙"的Unicode编码为4E59,当我们收到UTF-16字节流"594E",那么我们如何确定这个字节流是"甲"还是"乙"呢(UTF-16有小尾序和大尾序两种表示方法)?

Unicode规范中推荐的标记字节顺序的方法就是BOMByte Order Mark。 UCS编码中有一个叫做ZERO WIDTH NO-BREAK SPACE的字符,它的编码为FEFF。而FEFF在UCS中是不存在的字符,所以不应该出现在实际的字节流传输中。所以UCS规范建议我们在传输字节流前,先传输字符ZERO WIDTH NO-BREAK SPACE。因此,如果接受方接受到FEFF,则表明该字节流为大尾序;若为FFFE,则表明该字节流为小尾序。因此,ZERO WIDTH NO-BREAK SPACE又被称作BOM。 UTF-8没有像UTF-16大小尾序的表示方法,所以是不需要BOM来表明字节顺序的,但是也可以用BOM来表明编码方式,字符ZERO WIDTH NO-BREAK SPACE的UTF-8编码是EF BB BF,这也是UTF-8有含有BOM和没有BOM的原因。

更多编码方案

GBK等