很长一段时间,对于字符集和字符编码都是处于一种懵懵懂懂的状态,对于什么ascii、GB2312、UTF8、UTF16、UTF32、Unicode之类的更是绕的晕晕乎乎,从来没有正面去彻底搞清楚其中就里。
今天就在这里把这些乱七八糟的玩意彻底搞明白。
字符:
什么是字符?“我”就是一个字符,“你”也是一个字符,“他”也是一个字符。字符就是你在计算机屏幕之上看到的每一个单独的文字或符号,无关语言,当然,并非每一个字符你都能看到,比如说换行符之类的。
字符集:
顾名思义,字符集就是字符的集合,传说在很久很久(其实也没多久啦),有许多的字符集,英文的ascii,中文的GB2312……等等,巴拉巴拉的。
总之,每一种语言都有其相对应的字符集。就酱紫,你品,你细品,我滴乖乖,全世界那么多种语言,得有多少种字符集才够用啊?
当然,人类聪明的大脑自然能够想到解决之道,那就是Unicode通用字符集。哎呀,这个字符集那可了不得,是所有字符集的大哥大,是国际组织制定的可以容纳世界上所有文字和符号的字符编码方案。目前的Unicode字符分为17组编排,0x0000 至 0x10FFFF,每组称为平面(Plane),而每平面拥有65536个码位,共1114112个。
我滴乖乖,一百多万个字符容量,怎么也够用了。
综上,字符、字符集的概念,咱们就简单的说完了。
但是,字符集与计算机是相互独立的,因为计算机只认得00000000000000000100111101100000(Unicode中“你”的二进制编码),它既不认识“我”,也不认识“你”,想让它识别出来“你”“我”“他”,就还需要一种映射关系,将“你”与00000000000000000100111101100000映射起来,这种映射关系,就叫做:
字符编码:
(注意,字符编码是立足于指定字符集之上的,是字符集的编码形式,比如说文件之中标明UTF-8、UTF-16、UTF-32,使用的全是Unicode字符集,而ascii既代表了字符集,又代表了字符编码,所以导致许多人把这两种概念搞得有点混乱。)
好了,现在咱们有了映射关系的概念,也有了Unicode通用字符集,但是现在又有了新的问题,我要将“你”进行网络传输,肯定是要传输00000000000000000100111101100000这一大串,总共是二进制的32位,有朋友疑惑为什么是32位,因为只有32位才足够映射所有的Unicode字符,这种编码方式,就叫做UTF-32,意为每个传输字符占32位空间。
那么问题来了,大家应该注意到了,二进制的“你”开头有一堆0,如果我还要传一个字母“a”,那么以UTF-32的编码方式来传输,a 的二进制开头0更多,就是(“你”)00000000111001001011110110100000(“a”)00000000000000000000000001100001。
这是为什么呢?
那是因为,“你”这个字符在Unicode字符集中的十进制编码为20320,那么在二进制的情况下就是只需两个字节就够了,即:0100111101100000。而字符“a”在Unicode字符集中的十进制字符编码为97,而二进制要表示97,只需要一字节Byte,也就是8位就足够了:01100001。
显然,前面那一大串0,全没啥用,都是用来占位的,都是为了满足UTF-32每一个字符都应该占32位的规范。那么这显然是对存储空间或网络传输效益的极大浪费,因为我一个字母明明用一个字节就能表示,你非要用四个,这不是浪费是什么?
所以,在这种情况之下,可变长字符编码规则,即UTF-8、UTF-16出现了。
UTF-8是指为了表示一个字符,最小要用到二进制8位,即一个字节,最多理论上是6个字节,而UTF-16是指最小要用到二进制16位,即两个字节,最多理论上也是6个字节。
那么这两者是如何实现可变长的呢?计算机是如何识别用UTF-8、UTF-16编码的数据是由分别是由几个字节构成一个字符的呢?
道理很简单,在UTF-8的编码情况下,只要一个二进制字节以0开头,即0XXXXXXX(注意,所有描述中出现的X符号,皆是用来存储Unicode字符集的二进制字符编码),那该字节必然是一个单字节字符,所以只要遇到字节开头0,便以单字节形式进行解码。因为ascii字符集全是单字节字符,所以说UTF-8兼容ascii。
如果是多字节字符,则构成情况=头字节+身字节+身字节……这种形式。上面讲了单字节必然是以0开头,那么多字节必然是以1开头的了,可是既然多字节都是以1开头,那么如何区分哪个是头字节?哪个是身字节呢?我们接着往下看。
头字节:
头字节描述着该字符总共占用几个字节,如头字节110XXXXX意指该字符总共由2个字节构成,1110XXXX是指字符占3个字节、11110XXX是指字符占4个字节……以此类推。如此,眼尖的朋友应该注意到了,如果是头字节,必然必然是以11开头
身字节:
那么既然头字节以11开头,身字节用10开头,便可与其作以区分了,即10XXXXXX。于是乎,UTF-8的多字节字符构成方式就是这样:(2字节)(头)110XXXXX (身)10XXXXXX、(三字节)(头)1110XXXX (身)10XXXXXX (身)10XXXXXX
UTF-16与UTF-8的区别就在于,字符的最小传输大小是两字节,那么所有在两字节之内便可表达的字符便不再需要头字节了。这种编码方式对于字母来说依旧是不友好的,因为所有字母皆可用单字节表达,另一个字节仍然是无用状态,因为多了个无用字节,所以UTF-16不兼容单字节的ascii码。虽说如此,UTF-16字符编码对于汉字来说却是非常适用,因为如果去除了头标志,许多要在UTF-8编码规则下占用3个字节的汉字,在UTF-16编码规则下只需要占用2个字节。不过就目前的流行情况来看,使用UTF-8编码格式是主流。
另外除了上面所常遇到的编码,还有一种编码非常常见,叫做Ansi。相信许多同学都觉得眼熟,都见过,实际上这个编码很特殊,是windows系统上面的特例,它的全称叫做(American National Standards Institute美国国家标准协会),为什么说它特殊,因为它并不是一个编码的名称,而是一群编码的总称。
当运行在中文操作系统上面,它代表的是gb2312,当运行在英文操作系统上面时,它又代表着ascii……
它在每个地区的操作系统上面就分别代表着该地区操作系统上面默认的字符编码,所有说Ansi编码不能应用于网络传输,就像你和你身在另一个国家的朋友虽然都是用Ansi编码编写文档,互传给对方时,却各自看到的都是乱码,因为虽然都叫Ansi,实际编码方式却大相径庭。而影响它代表着什么编码的,就是你windows系统所设置的语言与地区。
哎呀,写到这里,总算把这些乱七八糟的给捋明白了,以后便不会再被各种字符问题绊脚。以上所述如何谬误之处,欢迎诸位朋友指正。