字符编码Unicode、UTF-8 ...

1. ASCII

我们知道,计算机内部,所有信息最终都是一个二进制值。每一个二进制位(bit)有0和1两种状态,因此八个二进制位就可以组合出256种状态,这被称为一个字节(byte)。也就是说,一个字节一共可以用来表示256种不同的状态,每一个状态对应一个符号,就是256个符号,从00000000到11111111。
上个世纪60年代,美国制定了一套字符编码,对英语字符与二进制位之间的关系,做了统一规定。这被称为 ASCII 码(American Standard Code for Information Interchange,美国信息互换标准代码),一直沿用至今。

当时世界上所有的计算机都用同样的ASCII方案来保存英文文字。

ASCII 码一共规定了128个字符的编码,比如空格SPACE是32(二进制00100000),大写的字母A是65(二进制01000001)。这128个符号(包括32个不能打印出来的控制符号),只占用了一个字节的后面7位,最前面的一位统一规定为0。

2. 扩展字符集

后来,世界各地都开始使用计算机,但是很多国家用的不是英文,他们的字母里有许多是ASCII里没有的,为了可以在计算机保存他们的文字,他们决定采用
127号之后的空位来表示这些新的字母、符号,还加入了很多画表格时需要用下到的横线、竖线、交叉等形状,一直把序号编到了最后一个状态255。从128 到255这一页的字符集被称”扩展字符集“。

比如,在法语中,字母上方有注音符号,它就无法用 ASCII 码表示。比如,法语中的é的编码为130(二进制10000010)。这样一来,这些欧洲国家使用的编码体系,可以表示最多256个符号。

但是,这里又出现了新的问题。不同的国家有不同的字母,因此,哪怕它们都使用256个符号的编码方式,代表的字母却不一样。比如,130在法语编码中代表了é,在希伯来语编码中却代表了字母Gimel (ג),在俄语编码中又会代表另一个符号。但是不管怎样,所有这些编码方式中,0--127表示的符号是一样的,不一样的只是128--255的这一段。

3. 中文编码GBK

等中国人们得到计算机时,已经没有可以利用的字节状态来表示汉字,况且有6000多个常用汉字需要保存呢。但是这难不倒智慧的中国人民,我们不客气地把那些127号之后的奇异符号们直接取消掉, 规定:一个小于127的字符的意义与原来相同,但两个大于127的字符连在一起时,就表示一个汉字,前面的一个字节(他称之为高字节)从0xA1用到0xF7,后面一个字节(低字节)从0xA1到0xFE,这样我们就可以组合出大约7000多个简体汉字了。在这些编码里,我们还把数学符号、罗马希腊的字母、日文的假名们都编进去了,连在 ASCII 里本来就有的数字、标点、字母都统统重新编了两个字节长的编码,这就是常说的”全角”字符,而原来在127号以下的那些就叫”半角”字符了。中国人民看到这样很不错,于是就把这种汉字方案叫做 “GB2312“。GB2312 是对 ASCII 的中文扩展。

但是中国的汉字太多了,于是我们不得不继续把GB2312 没有用到的码位找出来老实不客气地用上。后来还是不够用,于是干脆不再要求低字节一定是127号之后的内码,只要第一个字节是大于127就固定表示这是一个汉字的开始,不管后面跟的是不是扩展字符集里的内容。结果扩展之后的编码方案被称为 GBK 标准,GBK包括了GB2312 的所有内容,同时又增加了近20000个新的汉字(包括繁体字)和符号。

后来少数民族也要用电脑了,于是我们再扩展,又加了几千个新的少数民族的字,GBK扩成了 GB18030。从此之后,中华民族的文化就可以在计算机时代中传承了。 中国的程序员们看到这一系列汉字编码的标准是好的,于是通称他们叫做 “DBCS“(Double Byte Charecter Set 双字节字符集)。在DBCS系列标准里,最大的特点是两字节长的汉字字符和一字节长的英文字符并存于同一套编码方案里,因此他们写的程序为了支持中文处理,必须要注意字串里的每一个字节的值,如果这个值是大于127的,那么就认为一个双字节字符集里的字符出现了。

因为当时各个国家都像中国这样搞出一套自己的编码标准,结果互相之间谁也不懂谁的编码,谁也不支持别人的编码,连大陆和台湾这样只相隔了150海里,使用着同一种语言的兄弟地区,也分别采用了不同的 DBCS 编码方案——当时的中国人想让电脑显示汉字,就必须装上一个”汉字系统”,专门用来处理汉字的显示、输入的问题,像是那个台湾的愚昧封建人士写的算命程序就必须加装另一套支持 BIG5 编码的什么”倚天汉字系统”才可以用,装错了字符系统,显示就会乱了套!这怎么办?而且世界民族之林中还有那些一时用不上电脑的穷苦人民,他们的文字又怎么办?

4. Unicode

正在这时,一个叫 ISO(国际标谁化组织)的国际组织决定着手解决这个问题。他们采用的方法很简单:废了所有的地区性编码方案,重新搞一个包括了地球上所有文化、所有字母和符号 的编码!他们打算叫它”Universal Multiple-Octet Coded Character Set”,简称 UCS, 俗称 “unicode“。

Unicode只有一个字符集,中、日、韩的三种文字占用了Unicode中0x3000到0x9FFF的部分  
Unicode目前普遍采用的是UCS-2,它用两个字节来编码一个字符, 比如汉字"经"的编码是0x7ECF,注意字符编码一般用十六进制来 表示,为了与十进制区分,十六进制以0x开头,0x7ECF转换成十进制 就是32463,UCS-2用两个字节来编码字符,两个字节就是16位二进制, 2的16次方等于65536,所以UCS-2最多能编码65536个字符。 编码从0到127的字符与ASCII编码的字符一样,比如字母"a"的Unicode 编码是0x0061,十进制是97,而"a"的ASCII编码是0x61,十进制也是97, 对于汉字的编码,事实上Unicode对汉字支持不怎么好,这也是没办法的, 简体和繁体总共有六七万个汉字,而UCS-2最多能表示65536个,才六万 多个,所以Unicode只能排除一些几乎不用的汉字,好在常用的简体汉字 也不过七千多个,为了能表示所有汉字,Unicode也有UCS-4规范,就是用 4个字节来编码字符,不过现在普遍采用的还是UCS-2,只用两个字节来 编码。

Unicode 它仅仅只是一个字符集,规定了每个字符的数字编号是多少,至于这个二进制代码如何存储则没有任何规定。它的想法很简单,就是为每个字符规定一个用来表示该字符的数字,仅此而已。

之前提到,Unicode 没有规定字符对应的二进制码如何存储。以汉字“汉”为例,它的 Unicode 码点是 0x6c49,对应的二进制数是 110110001001001,二进制数有 15 位,这也就说明了它至少需要 2 个字节来表示。可以想象,在 Unicode 字典中往后的字符可能就需要 3 个字节或者 4 个字节,甚至更多字节来表示了。

这就导致了一些问题,计算机怎么知道你这个 2 个字节表示的是一个字符,而不是分别表示两个字符呢?这里我们可能会想到,那就取个最大的,假如 Unicode 中最大的字符用 4 字节就可以表示了,那么我们就将所有的字符都用 4 个字节来表示,不够的就往前面补 0。这样确实可以解决编码问题,但是却造成了空间的极大浪费,如果是一个英文文档,那文件大小就大出了 3 倍,这显然是无法接受的。

于是,为了较好的解决 Unicode 的编码问题, UTF-8 和 UTF-16 两种当前比较流行的编码方式诞生了。当然还有一个 UTF-32 的编码方式,也就是上述那种定长编码,字符统一使用 4 个字节,虽然看似方便,但是却不如另外两种编码方式使用广泛。

  • Unicode 是「字符集」
  • UTF-8 是「编码规则」

1、UTF-32 

这个就是字符所对应编号的整数二进制形式,四个字节,这个就是直接转换。 比如:马的Unicode为:U+9A6C,那么直接转化为二进制,它的表示就为:1001 1010 0110 1100。

注意:转换成二进制后计算机存储的问题。计算机在存储器中排列字节有两种方式:大端法和小端法,大端法就是将高位字节放到底地址处,比如0x1234, 计算机用两个字节存储,一个是高位字节0x12,一个是低位字节0x34,它的存储方式为下:

UTF-32用四个字节表示,处理单元为四个字节(一次拿到四个字节进行处理),如果不分大小端的话,那么就会出现解读错误,比如我们一次要处理四个字节 12 34 56 78,这四个字节是表示0x12 34 56 78还是表示0x78 56 34 12 ,不同的解释最终表示的值不一样。

我们可以根据他们高低字节的存储位置来判断他们所代表的含义,所以在编码方式中有 UTF-32BE 和 UTF-32LE ,分别对应大端和小端,来正确地解释多个字节(这里是四个字节)的含义。

2、UTF-16 

UTF-16使用变长字节表示 

① 对于编号在U+0000到U+FFFF的字符(常用字符集),直接用两个字节表示。 
② 编号在 U+10000到U+10FFFF之间的字符,需要用四个字节表示。

同样,UTF-16 也有字节的顺序问题(大小端),所以就有UTF-16BE表示大端,UTF-16LE表示小端。

3、UTF-8 

UTF-8就是使用变长字节表示,顾名思义,就是使用的字节数可变,这个变化是根据 Unicode 编号的大小有关,编号小的使用的字节就少,编号大的使用的字节就多。使用的字节个数从1到4个不等。

UTF-8的编码规则:

① 对于单字节的符号,字节的第一位设为0,后面的7位为这个符号的Unicode码,因此对于英文字母,UTF-8编码和ASCII码是相同的。 

② 对于n字节的符号(n>1),第一个字节的前n位都设为1,第n+1位设为0,后面字节的前两位一律设为10,剩下的没有提及的二进制位,全部为这个符号的Unicode码 。

举个例子:比如说一个字符的Unicode编码是130,显然按照UTF-8的规则一个字节是表示不了它(因为如果是一个字节的话前面的一位必须是0),所以需要两个字节(n = 2)。

根据规则,第一个字节的前 2 位都设为1,第3(2+1)位设为0,则第一个字节为:110X XXXX,后面字节的前两位一律设为10,后面只剩下一个字节,所以后面的字节为:10XX XXXX。所以它的格式为110XXXXX 10XXXXXX 。

Unicode编号范围与对应的UTF-8二进制格式 :

对于一个具体的Unicode编号,具体进行UTF-8的编码的方法:

首先找到该Unicode编号所在的编号范围,进而可以找到与之对应的二进制格式,然后将该Unicode编号转化为二进制数(去掉高位的0),最后将该二进制数从右向左依次填入二进制格式的X中,如果还有X未填,则设为0 。

比如:“马”的Unicode编号是:0x9A6C,整数编号是39532,对应第三个范围(2048 - 65535),其格式为:1110XXXX 10XXXXXX 10XXXXXX,39532 对应的二进制是 1001 1010 0110 1100,将二进制填入进入就为: 11101001 10101001 10101100 。

由于UTF-8的处理单元为一个字节(也就是一次处理一个字节),所以处理器在处理的时候就不需要考虑这一个字节的存储是在高位还是在低位,直接拿到这个字节进行处理就行了,因为大小端是针对大于一个字节的数的存储问题而言的。
 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值