您的位置:首页 > 其它

汉字编码到底是个什么玩意儿?

2015-06-04 23:05 771 查看
提起汉字编码,可能会让很多程序员头大。因为搞不清楚为什么会有这么多种编码(GBK、GB2312、Unicode、UTF-8、UTF-16等等等等),咱们统一一下不可以么?好吧,这个问题说来话长,那就让我们从头捋一捋吧。

首先,计算机被发明出来后就是二进制的,一个二进制为就是1bit,8个bit就组成了一个字节(Byte)。那每个字节呢就可以表示一个字符,并且一个字节可以表示的字符多达256种。最终,国际标准只是使用了00000000-01111111作为标准的ASCII字符集。本来,如果只有英文字母,阿拉伯数字和一些特殊符号(标点符号和控制符号)这个字符集就差不多够了。但是,计算机虽然是米国人发明的,也要让全世界人民一起玩。这样,对于字符的编码仅仅采用ASCII编码方式就远远不够了。就这样,高瞻远瞩的党国由中国国家标准总局1980年发布了信息交换用汉字编码字符集,也就是GBK2312-1980(简称GBK)。基本集共收入汉字6763个和非汉字图形字符682个。整个字符集分成94个区,每区有94个位。每个区位上只有一个字符,因此可用所在的区和位来对汉字进行编码,称为区位码。后来随着时间的推移又对该字符集进行了扩展,就形成了GBK(K:扩展),这个字符集向下兼容GB2312并收录了大量中日韩问字符。后来又扩展成了GB18030。其实,一看名字就知道它只是天朝自己发明的了。要想做一个国际通用的字符集,天朝政府是推动不了的。这样,为了解决传统的字符编码方案的局限性,为每种语言中的每个字符设定统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。统一码联盟(The
Unicode Consortium)于1990年开始了研发Unicode字符集,并于1994年公布了这一字符集。

但它只是一个字符集,要用它们来存储字符又会面临一个问题。比如你的unicode编码4F60,那么这个2个字节到底是代表“你”呢还是代表O`(ASCII码分别为4F和60),如果直接使用这一字符集而不加编码,就无法区分ASCII吗与Unicode编码。为了解决这个问题,也需要将区位码转换为机内码,其中一种编码方式,也就是UTF-8编码方式如下:

U-00000000 - U-0000007F: 0xxxxxxx

U-00000080 - U-000007FF: 110xxxxx 10xxxxxx

U-00000800 - U-0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx

U-00010000 - U-001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

U-00200000 - U-03FFFFFF: 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx

U-04000000 - U-7FFFFFFF: 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx

下面,让我们来做个实验:

打开记事本输入 你好吗 并保存。然后用UltraEdit打开,并打开其16进制编辑模式



这样就可以看到它们的16进制编码内容:



“你好吗”的区位码为(3667 0x2443)(2635 0x1A23)(3480 0x2250),将它转化为机内码加上A0A0很明显就是以上结果。

然后将该文件另存为(选择用UTF-8编码保存文件)后用UE打开,用16进制方式显示结果如下(UE中UTF-8编码文件16进制显示后后面对应汉字莫名乱码了。。)



其中以 “你” 为例,它对应的unicode编码为4F60(01001111 01100000)转换成UTF-8编码为11100100 1011110110100000(E4BDA0),这其实也就是unicode字符集,UTF-8编码

其实利用JAVA程序我们可以轻松地获得各种字符的编码信息

public static void main(String[] args) throws UnsupportedEncodingException {
String str = "测试a";
System.out.println(str.getBytes("ISO8859-1").length);
System.out.println(str.getBytes("GB2312").length);
System.out.println(str.getBytes("GBK").length);
System.out.println(str.getBytes("UTF-8").length);
}

通过控制台输出可知四种编码的长度分别为3 5 5 7,这也与我们的之前所说的是一致的,GB2312和GBK中的汉字字符占据2个字节空间,而UTF-8汉字字符每个占据了3个字节的空间,而ISO8859-1占据了1个字节的空间。这里又不免有个小小的疑问,为啥ISO8859-1的字符用一个字节就能表示一个汉字字符呢?很显然,这是不可能滴,是它无法表示汉字字符,所以,它返回的字节信息又是什么呢?

public static void main(String[] args) throws UnsupportedEncodingException {
byte[] b = "测试a".getBytes("ISO8859-1");
for(int i=0 ; i<b.length ; i++){
String str = Integer.toBinaryString(b[i]);
System.out.println(str);
}
}




原来两个无法表示的汉字字符返回的都是00111111,也就是 ? 对应的ascii值。既然说到这里了,顺道看看这段代码

public static void main(String[] args) throws UnsupportedEncodingException {
byte[] b = "测".getBytes("UTF-8");
for(int i=0 ; i<b.length ; i++){
String str = Integer.toHexString(b[i]);
System.out.println(b[i] + "," + str);
}
}

控制台输出如下



是不是觉得很奇怪,为啥每个字节转化成16进制字符串后都在前面加了6个 f 呢?那就以 e6 为例说明下,“测” 的第一个字节为 e6,当直接把这个字节传入toHexString中时它其实已经被强转为整型数字 -26 了,这下再用这个整型数字 -26 转化为16进制字符串时它就不再只占一个字节了,而是4个字节,所以再转成16进制字符串时就多了6个f。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: