由于接触到的编码方式比较多,比较杂,比较容易搞混,而且经常性的发生一些笑话,所以决定彻底的整理一下关于字符集的编码方式。
1. 编码表:拿汉语来说,就是一张可以表示全部汉子的表,每个汉子都对应一种编码,然后所有的这些编码组成的就叫做编码表。很多文章也叫做编码,但是为了和后面文章中要解释的UTF-8编码做出区分,我们称这样的叫做编码表。
2. ASCII(American Standard Code for Information Interchange)
这是计算机上最早使用的通用的编码方案。那个时候计算机还只是拉丁文字的专利。当时绝大部分专家都认为,要用计算机
,必须熟练掌握英文。这种编码占用7个Bit,在计算机中占用一个字节,8位,最高位没用,通讯的时候有时用作奇偶
校验位。
tips : 扩展ASCII 是将第8位进行利用,做出的一些扩展。也就是说,扩展的ASCII 有256个字符编码
3. ANSI(American National Standard Institite )
美国国家标准协会,也就是说,每个国家自己制定自己的文字的编码规则,并得到了ANSI认可,
符合ANSI的标准,全世界在表示对应国家文字的时候都通用这种编码就叫ANSI编码。换句话说,中国的ANSI编码和在日
本的ANSI的意思是不一样的,因为都代表自己国家的文字编码标准。比如中国的ANSI对应就是GB2312标准,日本就是
JIT标准,香港,台湾对应的是BIG5标准等等。--摘自百度.
总体来说,全世界有很多的编码表,每种语言都有自己不同的编码表,例如汉语的GB-2312,繁体的BIG-5,这样以来,想要同时表示不同的文字,就比较困难,于是引出了国际性的编码表,unicode编码表。
unicode编码表囊括了全世界所有文字的编码。
早期时候 , unicode 分为 UCS-2 和UCS-4 .
UCS-2 采用的是 两个字节的编码方式,UCS-4 用4个字节编码。
UCS-4编码 分为 group、plane(平面)、row (行)、cell(码位),每个占8个字节。
最高位group的最高位为0,于是group 有2^7=128个group , 每个group有256个平面,每个平面有256行,每行有256个码位。
所以每个平面拥有65536个码位,unicode 计划使用了17个平面,所以一共有17*65536 = 1114112个码位 。
第一个平面也就是 group[0] 就是 UCS-2 编码,采用了两个字节编码,只能表示65536 个码位,
在group[0]中表示出了基础的、常用的字符编码,所以称之为BMP(Basic Multilingual Plane)就是常用字符集。
好了,已经说到了这里,想必已经理解了unicode 编码表和 各种语言编码编码表的区别了吧。
下面我们来说一说,unicode编码表的 编码方式。
目前主流的 编码方式: UTF-8 、 UTF-16、UTF-32 。 三种编码方式 。
编码方式是将编码表中的编码按照一定的格式组合存储起来。
首先说说UTF-8 ,这是一种变长字节的编码方式,变长也就是多个字节混合在一起的编码方式。
优点 :
UTF-8编码可以通过屏蔽位和移位操作快速读写。 UTF-8 是字节顺序无关的。它的字节顺序在所有系统中都是一样的,因此它实际上并不需要BOM。而且UTF-8相对于其他编码方式,在网络传输中省流量,占用的字节数较小。
缺点:
你无法从UNICODE字符数判断出UTF-8文本的字节数,因为UTF-8是一种变长编码,它需要用2个字节编码那些扩展ASCII字符集只需一个字节的字符。所以在这种情况下,用UTF-8进行编码是比较浪费的。
下面说UTF-8 的编码原理:
对于某一个字符的UTF-8编码,如果只有一个字节则其最高二进制位为0;如果是多字节,其第一个字节从最高位开始,连续的二进制位值为1的个数决定了其编码的位数,其余各字节均以10开头。UTF-8最多可用到6个字节。
如表:
1字节 0xxxxxxx
2字节 110xxxxx 10xxxxxx
3字节 1110xxxx 10xxxxxx 10xxxxxx
4字节 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
5字节 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
6字节 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
因此UTF-8中可以用来表示字符编码的实际位数最多有31位,即上表中x所表示的位。除去那些控制位(每字节开头的10等),这些x表示的位与UNICODE编码是一一对应的,位高低顺序也相同。
实际将UNICODE转换为UTF-8编码时应先去除高位0,然后根据所剩编码的位数决定所需最小的UTF-8编码位数。
因此那些基本ASCII字符集中的字符(UNICODE兼容ASCII)只需要一个字节的UTF-8编码(7个二进制位)便可以表示。
在来说一说UTF-16编码:
Unicode的编码空间从U+0000到U+10FFFF,共有1,112,064个码位(code point)可用来映射字符. Unicode的编码空间可以划分为17个平面(plane),每个平面包含216(65,536)个码位。17个平面的码位可表示为从U+xx0000到U+xxFFFF,其中xx表示十六进制值从0016到1016,共计17个平面。第一个平面称为基本多语言平面(Basic Multilingual Plane, BMP),或称第零平面(Plane 0)。其他平面称为辅助平面(Supplementary Planes)。基本多语言平面内,从U+D800到U+DFFF之间的码位区段是永久保留不映射到Unicode字符。UTF-16就利用保留下来的0xD800-0xDFFF区段的码位来对辅助平面的字符的码位进行编码。
从U+0000至U+D7FF以及从U+E000至U+FFFF的码位
第一个Unicode平面(码位从U+0000至U+FFFF)包含了最常用的字符。该平面被称为基本多语言平面,缩写为BMP(Basic Multilingual Plane, BMP)。UTF-16与UCS-2编码这个范围内的码位为16比特长的单个码元,数值等价于对应的码位. BMP中的这些码位是仅有的可以在UCS-2中表示的码位.
从U+10000到U+10FFFF的码位
辅助平面(Supplementary Planes)中的码位,在UTF-16中被编码为一对16比特长的码元(即32bit,4Bytes),称作代理对(surrogate pair),具体方法是:
lead \ trail | DC00 | DC01 | … | DFFF |
---|---|---|---|---|
D800 | 10000 | 10001 | … | 103FF |
D801 | 10400 | 10401 | … | 107FF |
⋮ | ⋮ | ⋮ | ⋱ | ⋮ |
DBFF | 10FC00 | 10FC01 | … | 10FFFF |
- 码位减去0x10000,得到的值的范围为20比特长的0..0xFFFFF.
- 高位的10比特的值(值的范围为0..0x3FF)被加上0xD800得到第一个码元或称作高位代理(high surrogate),值的范围是0xD800..0xDBFF.由于高位代理比低位代理的值要小,所以为了避免混淆使用,Unicode标准现在称高位代理为前导代理(lead surrogates).
- 低位的10比特的值(值的范围也是0..0x3FF)被加上0xDC00得到第二个码元或称作低位代理(low surrogate),现在值的范围是0xDC00..0xDFFF.由于低位代理比高位代理的值要大,所以为了避免混淆使用,Unicode标准现在称低位代理为后尾代理(trail surrogates).
上述算法可理解为:辅助平面中的码位从U+10000到U+10FFFF,共计FFFFF个,即220=1,048,576个,需要20位的空间来表示。如果用两个16位长的整数组成的序列来表示,第一个整数(称为前导代理)要容纳上述20位空间的前10位,第二个整数(称为后尾代理)容纳容纳上述20位空间的后10位。还要能根据16位整数的值直接判明属于前导整数代理的值的范围(210=1024),还是后尾整数代理的值的范围(也是210=1024)。因此,需要在基本多语言平面中保留不对应于Unicode字符的2048个码位,就足以容纳前导代理与后尾代理所需要的编码空间。这对于基本多语言平面总计65536个码位来说,仅占3.125%.
由于前导代理、后尾代理、BMP中的有效字符的码位,三者互不重叠,搜索是简单的:一个字符编码的一部分不可能与另一个字符编码的不同部分相重叠。这意味着UTF-16是自同步(self-synchronizing):可以通过仅检查一个码元就可以判定给定字符的下一个字符的起始码元. UTF-8也有类似优点,但许多早期的编码模式就不是这样,必须从头开始分析文本才能确定不同字符的码元的边界.
由于最常有的字符都在基本多文种平面中,许多软件的处理代理对的部分往往得不到充分的测试。这导致了一些长期的bug与潜在安全漏洞,甚至在广为流行得到良好评价的应用软件[1].
从U+D800到U+DFFF的码位
Unicode标准规定U+D800..U+DFFF的值不对应于任何字符.
但是在使用UCS-2的时代,U+D800..U+DFFF内的值被占用,用于某些字符的映射。但只要不构成代理对,许多UTF-16编码解码还是能把这些不符合Unicode标准的字符映射正确的辨识、转换成合规的码元[2].按照Unicode标准,这种码元串行本来应算作编码错误.
示例:UTF-16编码
假设要将U+64321 (16进位)转成UTF-16编码.因为它超过U+FFFF,所以他必须编译成32位(4个byte)的格式,如下所示:11
V = 0x64321 Vx = V - 0x10000 = 0x54321 = 0101 0100 0011 0010 0001 Vh = 01 0101 0000 // Vx的高位部份的10 bits Vl = 11 0010 0001 // Vx的低位部份的10 bits w1 = 0xD800 //結果的前16位元初始值 w2 = 0xDC00 //結果的後16位元初始值 w1 = w1 | Vh = 1101 1000 0000 0000 | 01 0101 0000 = 1101 1001 0101 0000 = 0xD950 w2 = w2 | Vl = 1101 1100 0000 0000 | 11 0010 0001 = 1101 1111 0010 0001 = 0xDF21
所以这个字U+64321最后正确的UTF-16编码应该是:
0xD950 0xDF21
而在小尾序中最后的编码应该是:
0x50D9 0x21DF
因为这个字超过U+FFFF所以无法用UCS-2的格式编码
16进制编码范围 | UTF-16表示方法(二进制) | 10进制码范围 | 字节数量 |
---|---|---|---|
U+0000---U+FFFF | xxxxxxxx xxxxxxxx yyyyyyyy yyyyyyyy | 0-65535 | 2 |
U+10000---U+10FFFF | 110110yyyyyyyyyy 110111xxxxxxxxxx | 65536-1114111 | 4 |
UTF-16比起UTF-8,好处在于大部分字符都以固定长度的字节(2字节)存储,但UTF-16却无法兼容于ASCII编码。
(以上UTF-16 的解释摘自维基百科)
解释完UTF16 接下来 介绍 UTF-32。
UTF-32和Unicode码表基本一一对应,固定四个字节。
为什么不采用UTF-32呢,因为unicode定义的范围太大了,其实99%的人使用的字符编码不会超过2个字节,所以如同统
一用4个字节,简单倒是简单了,但是数据冗余确实太大了,不好,所以16位是最好的。就算遇到超过16位能表示的字
符,我们也可以通过上面讲到的代理技术,采用32位标识,这样的方案是最好的。所以现在绝大部分机器实现unicode
还是采用的utf-16的方案。
还想说一下,肯定会有人问为什么会有 大尾序小尾序这样的说法。
因为UTF-16只是定义了如何将Unicode字符映射为一个或多个16位二进制数,但却没有定义16位二进制数怎么用字节来表示。如果用两个字节来表示这个16位二进制数,将这个数的低八位放在前面的字节,就叫做小尾序,即UTF-16LE(这是Windows平台通常的处理方式)。
最后 说 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头只是建议添加,不是强制的。
结束 。
希望 能对 编码 有疑惑的程序猿们,有所帮助,有问题的,可以随时交流。