字符集和编码格式的关系、浅析ascii、asci、gbk、unicode、utf-8之间的关系

26 篇文章 0 订阅
19 篇文章 0 订阅

 

前言

一开始人类的世界只有电路的通断和电位的高低,后来有一群人,他们将电位的变化进行组合,构成了更加复杂,更多变化的电路,人们联想到这种电路的其中一种组合不正好可以表示一种状态,那么规定其中一种状态为一个数字,正好就可以进行数学运算,于是经过电路设计和实验,出现了加法运算器、减法运算器、乘法运算器、除法运算器等等等。再后来,有一群人他们将8个电位进行组合,构成了用来表示某种状态的基本单元,称之为“字节”。随着发展,出现了集数学运算和存储表示为一体的机器“计算机”。

计算机的本质是一种状态机,其本身不能直接表示现实世界的任何东西,所有现实世界的东西要想在计算机内部进行表示,就必须建立自身与计算机内部的状态的映射关系,而编码正好就是建立这种映射关系的一种方式。

一、字符集

字符集是对一些字符的集合,其规定了字符的码值,码值=计算机内部的表示状态。如ascii码,其包含了所有的英文字母的计算机内部表示,也包含一些特殊符号的表示。GBK则包含一些中文字符的计算机内部表示。UNICODE其包含了全世界大多数的字符的计算机内部表示,是一个非常大的集合。

二、编码格式

编码格式是字符的码值在计算机内部具体的实现方式,其定义了一个字符的码值在计算机内应该如何存储

三、一些字符集及其编码格式

1、ASCII码

ASCII是美国标准信息交换码,其包含了所有的英文字母和一些特殊字符的码值的规定。同时该字符集也规定了码值在计算机内部的存储方式,即单字节存储。

2、ASCI

ASCI字符集是兼容了ASCII码的各国自己实现的一种字符集。

  美国国家标准协会,也就是说,每个国家(非拉丁语系国家)自己制定自己的文字的编码规则,并得到了ANSI认可,符合ANSI的标准,全世界在表示对应国家文字的时候都通用这种编码就叫ANSI编码。换句话说,中国的ANSI编码和在日本的ANSI的意思是不一样的,因为都代表自己国家的文字编码标准。比如中国的ANSI对应就是GB2312标准,日本就是JIT标准,香港,台湾对应的是BIG5标准等等。当然这个问题也比较复杂,微软从95开始,用就是自己搞的一个标准GBK。GBK兼容了GB2312。

同时每一个ASCI字符集都有对应的自己的计算机内部的编码格式,即计算机内部的表示的实现方式。

GB2312最常见的编码格式就是ECU_CN。

GBK则采用的是双字节的表示方法。这两种字符集都直接定义了字符的编码同时也定义了编码格式,即直接采用双字节的编码格式

 

3、unicode

 这是一个编码方案,说白了就是一张包含全世界所有文字的一个编码表,不管你用的上,用不上,不管是现在用的,还是以前用过的,只要这个世界上存在的文字符号,统统给你一个唯一的编码,这样就不可能有任何冲突了。不管你要同时显示任何文字,都没有问题。因此在这样的方案下,Unicode出现了。Unicode编码范围是:0-0x10FFFF,可以容纳1114112个字符,100多万啊。全世界的字符根本用不完了,Unicode 5.0版本中,才用了238605个码位。所以足够了。

因此从码位范围看,严格的unicode需要3个字节来存储。但是考虑到理解性和计算机处理的方便性,理论上还是用4个字节来描述。

   Unicode采用的汉字相关编码用的是《CJK统一汉字编码字符集》—国家标准 GB13000.1 是完全等同于国际标准《通用多八位编码字符集(UCS)》 ISO 10646.1。《GB13000.1》中最重要的也经常被采用的是其双字节形式的基本多文种平面。在这65536个码位的空间中,定义了几乎所有国家或地区的语言文字和符号。其中从0x4E00到 0x9FA5 的连续区域包含了 20902 个来自中国(包括台湾)、日本、韩国的汉字,称为 CJK (Chinese Japanese Korean) 汉字。CJK是《GB2312-80》、《BIG5》等字符集的超集。

  CJK包含了中国,日本,韩国,越南,香港,也就是CJKVH。这个在UNICODE的Charsetchart中可以明显看到。  unicode的相关标准可以从unicode.org上面获得,目前已经进行到了6.0版本。

 

目前unicode的编码格式主要有以下三种:

(1)、utf-8
   这个方案的意思以8位为单位来标识文字,注意并不是说一个文字用8位标识。他其实是一种MBCS方案,可变字节的。到底需要几个字节表示一个符号,这个要根据这个符号的unicode编码来决定,最多4个字节。 目前这种方案已经成为RFC3629标准。

编码规则如下:
   Unicode编码(16进制)  UTF-8 字节流(二进制)  
 000000 - 00007F  0xxxxxxx   
000080 - 0007FF  110xxxxx 10xxxxxx   
000800 - 00FFFF  1110xxxx 10xxxxxx 10xxxxxx   
010000 - 10FFFF  11110xxx 10xxxxxx 10xxxxxx 10xxxxxx   
UTF-8的特点是对不同范围的字符使用不同长度的编码。对于0x00-0x7F之间的字符,UTF-8编码与ASCII编码完全相同。

UTF-8编码的最大长度是4个字节。从上表可以看出,4字节模板有21个x,即可以容纳21位二进制数字。Unicode的最大码位0x10FFFF也只有21位。   

例1:“汉”字的Unicode编码是0x6C49。0x6C49在0x0800-0xFFFF之间,使用用3字节模板了:1110xxxx 10xxxxxx 10xxxxxx。将0x6C49写成二进制是:0110 1100 0100 1001, 用这个比特流依次代替模板中的x,得到:11100110 10110001 10001001,即E6 B1 89。   

例2:Unicode编码0x20C30在0x010000-0x10FFFF之间,使用用4字节模板了:11110xxx 10xxxxxx 10xxxxxx 10xxxxxx。

将0x20C30写成21位二进制数字(不足21位就在前面补0):0 00100000 1100 0011 0000,用这个比特流依次代替模板中的x,得到:11110000 10100000 10110000 10110000,即F0 A0B0 B0。

 

(2)、utf-16

 UTF-16编码以16位无符号整数为单位。注意是16位为一个单位,不表示一个字符就只有16位。现在机器上的unicode编码一般指的就是UTF-16。绝大部分2个字节就够了,但是不能绝对的说所有字符都是2个字节。这个要看字符的unicode编码处于什么范围而定,有可能是2个字节,也可能是4个字节。这点请注意!

下面算法解释来自百度百科。

我们把Unicode  unicode编码记作U。编码规则如下: 
  如果U<0x10000,U的UTF-16编码就是U对应的16位无符号整数(为书写简便,下文将16位无符号整数记作WORD)。如果U≥0x10000,我们先计算U'=U-0x10000,然后将U'写成二进制形式:yyyy yyyy yyxx xxxx xxxx,U的UTF-16编码(二进制)就是:110110yyyyyyyyyy110111xxxxxxxxxx。为什么U'可以被写成20个二进制位?Unicode的最大码位是0x10ffff,减去0x10000后,U'的最大值是0xfffff,所以肯定可以用20个二进制位表示。

   例如:Unicode编码0x20C30,减去0x10000后,得到0x10C30,写成二进制是:0001 0000 1100 0011 0000。用前10位依次替代模板中的y,用后10位依次替代模板中的x,就得到:1101100001000011 1101110000110000,即0xD8430xDC30。   

   按照上述规则,Unicode编码0x10000-0x10FFFF的UTF-16编码有两个WORD,第一个WORD的高6位是110110,第二个WORD的高6位是110111。可见,第一个WORD的取值范围(二进制)是11011000 00000000到11011011 11111111,即0xD800-0xDBFF。第二个WORD的取值范围(二进制)是11011100 00000000到11011111 11111111,即0xDC00-0xDFFF。为了将一个WORD的UTF-16编码与两个WORD的UTF-16编码区分开来,Unicode编码的设计者将0xD800-0xDFFF保留下来,并称为代理区(Surrogate):   

D800-DB7F  High Surrogates  高位替代   
DB80
-DBFF  High PrivateUse Surrogates  高位专用替代   
DC00
-DFFF  LowSurrogates  低位替代   
  高位替代就是指这个范围的码位是两个WORD的UTF-16编码的第一个WORD。低位替代就是指这个范围的码位是两个WORD的UTF-16编码的第二个WORD。那么,高位专用替代是什么意思?我们来解答这个问题,顺便看看怎么由UTF-16编码推导Unicode编码。   

 如果一个字符的UTF-16编码的第一个WORD在0xDB80到0xDBFF之间,那么它的Unicode编码在什么范围内?我们知道第二个WORD的取值范围是0xDC00-0xDFFF,所以这个字符的UTF-16编码范围应该是0xDB80 0xDC00到0xDBFF 0xDFFF。我们将这个范围写成二进制:  1101101110000000 11011100 00000000 - 11011011111111111101111111111111   按照编码的相反步骤,取出高低WORD的后10位,并拼在一起,得到   1110 0000 0000 0000 0000 -1111 1111 11111111 1111  即0xe0000-0xfffff,按照编码的相反步骤再加上0x10000,得到0xf0000-0x10ffff。这就是UTF-16编码的第一个WORD在0xdb80到0xdbff之间的Unicode编码范围,即平面15和平面16。因为Unicode标准将平面15和平面16都作为专用区,所以0xDB80到0xDBFF之间的保留码位被称作高位专用替代。

 

(3)、utf-32

这个就简单了,和Unicode码表基本一一对应,固定四个字节。
 为什么不采用UTF-32呢,因为unicode定义的范围太大了,其实99%的人使用的字符编码不会超过2个字节,所以如同统一用4个字节,简单倒是简单了,但是数据冗余确实太大了,不好,所以16位是最好的。就算遇到超过16位能表示的字符,我们也可以通过上面讲到的代理技术,采用32位标识,这样的方案是最好的。所以现在绝大部分机器实现unicode还是采用的utf-16的方案。当然也有UTF-8的方案。比如windows用的就是UTF16方案,不少linux用的就是utf8方案。

 

由于一种字符集有多种编码格式,所以就出现了编码格式检测的问题。

编码格式的检测

到底采用什么编码,如果能检测就好了。专家们也是这么想的,所以专家给每种格式和字节序规定了一些特殊的编码,这些编码在unicode 中是没有使用的,所以不用担心会冲突。这个叫做BOM(Byte Order Mark)头。意思是字节序标志头。通过它基本能确定编码格式和字节序。

UTF编码  Byte Order Mark   
UTF-8   EF BB BF   
UTF-16LE  FF FE   
UTF-16BE  FE FF   
UTF-32LE  FF FE 00 00   
UTF-32BE  00 00 FE FF
所以通过检测文件前面的BOM头,基本能确定编码格式和字节序。
但是这个BOM头只是建议添加,不是强制的,所以不少软件和系统没有添加这个BOM头(所以有些软件格式中有带BOM头和NoBOM头的选择),这个时候要检测什么格式,就比较麻烦了,当然可以检测,但是不能保证100%准确,只能通过编码范围从概率上来检查,虽然准确度还是比较高,但是不能保证100%。所以,时常看到检测错误的软件,也不奇怪了。

 

以上unicode部分来源于网络。如有侵权,请指出。

 

总结:

在程序设计的过程中,最好采用unicode,这样也方便了程序的国际化,只要有字库,则就能很好的显示。

在传输的过程中最好使用utf-8,那样就保证了传输之后的正确性。

最后附录几个概念:

CodePage:代码页,最早来自IBM,后来被微软,oracle,SAP等广泛采用。因为ANSI编码每个国家都不统一,不兼容,可能导致冲突,所以一个系统在处理文字的时候,必须要告诉计算机你的ANSI是哪个国家和地区的标准,这种国家和标准的代号(其实就是字符编码格式的代号),微软称为Codepage代码页,其实这个代码页和字符集编码的意思是一样的。

MBCS:多字节字符系统或者字符集

UTF:Unicode Transformation Format,unicode转换格式

BOM : Byte Order Mark字节序标志头,通过它基本能确定编码格式和字节序。

 

附上小故事一则:

记事本里面“联通”,然后保存,再次打开,则无法显示。

原因:当我们在输入“联通”这两个字的时候,我们采用的是GB系列的编码方式,但是保存的时候发生了误会,默认采用ASCI,也就是采用了相应字符集的编码格式,即采用了GB系列的编码格式。“联通”的内码是:      c1 1100 0001      aa 1010 1010      cd 1100 1101      a8 1010 1000  第一二个字节、第三四个字节的起始部分的都是"110"和"10",正好与UTF8规则里的两字节模板是一致的,于是再次打开记事本时,记事本就误认为这是一个UTF8编码的文件,让我们把第一个字节的110和第二个字节的10去掉,我们就得到了"00001 101010",再把各位对齐,补上前导的0,就得到了"0000 0000 0110 1010",不好意思,这是UNICODE的006A,也就是小写的字母"j",而之后的两字节用UTF8解码之后是0368,这个字符什么也不是。这就是只有"联通"两个字的文件没有办法在记事本里正常显示的原因

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值