汉字编码到底是个什么玩意儿?
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程序我们可以轻松地获得各种字符的编码信息
通过控制台输出可知四种编码的长度分别为3 5 5 7,这也与我们的之前所说的是一致的,GB2312和GBK中的汉字字符占据2个字节空间,而UTF-8汉字字符每个占据了3个字节的空间,而ISO8859-1占据了1个字节的空间。这里又不免有个小小的疑问,为啥ISO8859-1的字符用一个字节就能表示一个汉字字符呢?很显然,这是不可能滴,是它无法表示汉字字符,所以,它返回的字节信息又是什么呢?
原来两个无法表示的汉字字符返回的都是00111111,也就是 ? 对应的ascii值。既然说到这里了,顺道看看这段代码
控制台输出如下
是不是觉得很奇怪,为啥每个字节转化成16进制字符串后都在前面加了6个 f 呢?那就以 e6 为例说明下,“测” 的第一个字节为 e6,当直接把这个字节传入toHexString中时它其实已经被强转为整型数字 -26 了,这下再用这个整型数字 -26 转化为16进制字符串时它就不再只占一个字节了,而是4个字节,所以再转成16进制字符串时就多了6个f。
首先,计算机被发明出来后就是二进制的,一个二进制为就是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。
相关文章推荐
- 第一章第5题
- java实验报告三
- 如何学好C++语言
- java.io.FileNotFoundException: /storage/emulated/0/
- Eclipse中Ant的配置与测试
- linux配置java环境变量(详细) [转]
- DEVICE_ATTR
- 【数据库基础概念与数据抽象】竹外桃花三两枝——数据库学习之路(2)
- [leetcode] Excel Sheet Column Title
- 算法之递归练习
- 畅所欲言
- near指针,far指针,huge指针
- java:IO流学习小结
- 猴子运香蕉
- C++图像处理 -- 数据类型及公用函数
- LPC1788--I2C设置驱动PCF8574 与特别注意事项
- LPC1788---串口设置
- 空间解析几何
- java实验三实验报告
- android开发之播放音乐的方法