好久没有来锄草了,最近遇到编码问题,记录一下
主要包括区位码,GB,GB2312,UNICODE,UTF-8,ISO8859-1。大部分为转载,如有错误,请高人指正。
GB2312和ISO8859-1无法直接转换,这个问题头疼,不过网上还是有解决方案的,如果是这个问题,大家就狗狗一下吧。
以下内容主要来自:http://www.mao2.com/papers/zhongwenbianma.html
和
http://hi.baidu.com/cuifenghui/blog/item/735b55da1804a1dfb7fd4817.html
1.区位码:
我国国家标准汉字信息交换用编码,全称《信息交换用汉字编码字符集基本集》,标准号为 GB 2312-80(GB 是“国标”二字的汉语拼音缩写),由中华人民共和国国家标准总局发布,1981年5月1日实施,新加坡等地也使用这一编码。区位码是一个四位的十进制数,它的前两位叫做区码,后两位叫做位码。区位码的编码范围是:0101~9494。比如,按拼音排序后的第一个汉字是“啊”,它的区位码为1601(十六进制为1001 H)。下图是新华字典所显示的汉字“啊”的信息。
2. 汉字国标码(GB):
又称交换码,是一个码集。将区位码的十进制行列值转换成16进制,再分别加上20H得到。举例:将“万”的区位码转换为16进制表示:2D 52,分别加上20H得:4D 72,即为国标码。
由此,汉字第一个字节的最小值:10H + 20H =30H, 最大值:57H + 20H = 77H
第二个字节最小值: 01H + 20H = 21H, 最大值: 5EH + 20H = 7EH
3.GB2312 码:
又称为国标码,在区位码的基础上,分别将区号值和位号值各加 32(20H)而得到,于是它的编码范围是2121H~7E7EH,于是“啊”的GB2312 的编码为3021 H(即1001H+2020H)。但是,因为GB2312 与ASCII有重叠,通行方法是将GB2312 码两个字节的最高位置1以示区别,编码范围变为 0A1A1H~0FEFEH(实际只用到0xF7FEH)。这种在第八位置"1",提示计算机转入双字节的编码方式也叫EUC(Extended UNIX Code)编码,所以“啊”的GB2312 的最终编码为B0A1 H(即3021 H+8080H)。其实GB2312 和区位码是一样的,应该合在一起谈。但事实上两个的编码又不一样,我也不知为什么要弄得如此复杂。或许可以这样理解:区位码是一个编码方案,而 GB2312 编码则是这个方案在计算机里的一个具体实现。GB2312 将收录的汉字分成两级:第一级是常用汉字计3755个,置于16-55区,按汉语拼音字母/笔形顺序排列;第二级汉字是次常用汉字计3008个,置于 56-87区,按部首/笔画顺序排列。共计6763个汉字。
GB2312(1980年)一共收录了7445个字符,包括6763个汉字和682个其它符号。汉字区的内码范围高字节从B0-F7,低字节从A1-FE,占用的码位是72*94=6768。其中有5个空位是D7FA-D7FE。GB2312-80中共收录了7545个字符,用两个字节编码一个字符。每个字符最高位为0。GB2312-80编码简称国标码。
GB2312支持的汉字太少。1995年的汉字扩展规范GBK1.0收录了21886个符号,它分为汉字区和图形符号区。汉字区包括21003个字符。
4. UNICODE
Unicode字符集(简称为UCS),国际标准组织于1984年4月成立ISO/IECJTC1/SC2/WG2工作组,针对各国文字、符号进行统一性编码。1991年美国跨国公司成立UnicodeConsortium,并于1991年10月与WG2达成协议,采用同一编码字集。目前Unicode是采用16位编码体系,其字符集内容与ISO10646的BMP(BasicMultilingual Plane)相同。Unicode于1992年6月通过DIS(DrafInternationalStandard),目前版本V2.0于1996公布,内容包含符号6811个,汉字20902个,韩文拼音11172个,造字区6400个,保留20249个,共计65534个。Unicode编码后的大小是一样的.例如一个英文字母"a" 和 一个汉字"好",编码后都是占用的空间大小是一样的,都是两个字节!
Unicode可以用来表示所有语言的字符,而且是定长双字节(也有四字节的)编码,包括英文字母在内。所以可以说它是不兼容iso8859-1编码的,也不兼容任何编码。不过,相对于iso8859-1编码来说,uniocode编码只是在前面增加了一个0字节,比如字母'a'为"0061"。
需要说明的是,定长编码便于计算机处理(注意GB2312/GBK不是定长编码),而unicode又可以用来表示所有字符,所以在很多软件内部是使用unicode编码来处理的,比如java。
随着国际互联网的迅速发展,要求进行数据交换的需求越来越大,不同的编码体系越来越成为信息交换的障碍,而且多种语言共存的文档不断增多,单靠代码页已很难解决这些问题,于是UNICODE应运而生。
UNICODE 通常用作涉及双字节字符编码方案的通用术语。UNICODE CCS 3.1(CCS:编码字符集,coded character set) 的官方称谓是 ISO10646-1 通用多八字节编码字符集(Universal Multiple Octet Coded Character Set,UCS)。
UNICODE 编码系统可分为编码方式和实现方式两个层次。
目前用于实用的 UNICODE 版本的编码方式使用16位的编码空间,也就是每个字符占用2个字节。这样理论上一共最多可以表示 65536 个字符,基本满足各种语言的使用。标准的UNICODE编码方案使用32位的的编码空间,目前编码范围是0到10FFFF H。例如“啊”的UNICODE编码为55 4A H。
UNICODE 的实现方式不同于编码方式。一个字符的 UNICODE 编码是确定的。但是在实际传输过程中,由于不同系统平台对非ASCII码的处理都不太一致,以及出于节省空间的目的,对 UNICODE 编码的实现有着不同的方式。UNICODE 的实现方式称为UNICODE转换格式(UNICODE Translation Format,简称为 UTF)。常用的有:
1)UTF-8, 8bit编码, ASCII不作变换, 其他字符做变长编码, 每个字符1-6 byte。其与UNICODE编码的关系如下:
U-00000000-U-0000007F: 0xxxxxxx
U-00000080-U-000007FF: 110xxxxx 10xxxxxx
U-00000800-U-0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx
4-6位的UTF-8编码对应着U-00010000-U-7FFFFFFF,比较少用到,就不列出了。
“啊”的UTF-8编码是E5 95 8A,占3字节。
2)UTF-16,16位编码,基本上就是UNICODE编码的实现。这里只能说是“基本上”,因为对于码值在10000H到10FFFFH之间的 UNICODE码,UTF-16是使用“代理对”(即两个UNICODE码)来实现编码的。幸好,我们常用的字符,包括各国的文字、符号等,都可以使用单个UTF-16来编码,不需要通过代理对实现。因此,UTF-16是UNICODE编码实现的指定首选编码方式。例如“啊”的UTF-16编码是55 4A H,占2字节,并且与实际“啊”的实用16位UNICODE编码值是相等的。
3)UTF-32,32位编码,顾名思义,与标准UNICODE编码是一一对应的。例如“啊”的UTF-32编码是00 00 55 4A,占4字节。
上面罗列了一堆编码,希望大家还没有看晕过去。我们现在分析几种JAVA中的乱码问题。
首先,我们看看Window是如何处理文本文件的。Window2000内核是使用UNICODE编码进行字符处理的。Window上的纯文本编辑器(包括 JBuilder等JAVA开发工具的编辑器),在读进一个纯文本文件时,如果该文件本身没有指定编码格式,则编辑器会使用当前Window的代码页(中文Window下是CP936,即GBK)对文件内容字节流进行转换,转换成UNICODE编码。在保存时,则将内存中的UNICODE编码通过代码页转换为本地编码,以字节流形式存入磁盘。
JAVA核心也使用UNICODE进行字符处理,与Window2000是一致的,因此,处理文件的过程也和上面是一样的。因为internet传输也是以字节流方式传输的,因此JAVA在处理网络字节流时也按上述方式进行。在这种byte->char->byte类型的转换中,如果中间出现编码转换错误,就会冒出中文乱码问题了。
案例一:JAVA产生的文件(或控制台输出),在Window下正常,上传到UNIX运行时乱码。这是因为两台机器的代码页不一致造成的。解决办法:将UNIX上的语言区域设为与Window的一致后,再运行JAVA程序。
案例二:文本文件使用Window文本编辑器可以正常打开,而JAVA应用程序读入该文件时乱码。原因是文本文件有多种的编码方式包括ASCII,UTF- 8,UTF-16(而CPU有着字节顺序的问题,故UTF-16又细分为UTF-16LE小尾序、UTF-16BE大尾序两种编码)。文本文件在缺省下是根据当前系统的区域选定的编码方式来保存的,Window下也就是代码页。解决办法:检查该文本文件的编码方式,并在JAVA程序中按该编码方式读取数据。
5. UTF-8
在上面已经讲述,其实是包含关系。
6. ISO8859-1:
属于单字节编码,最多能表示的字符范围是0-255,应用于英文系列。比如,字母'a'的编码为0x61=97。
最常用的是ASCII编码,编码范围是00 H-7F H。ISO8859编码是对ASCII的扩展,扩展部分是80 H-FF H,用于定义其它非英文字母,如法语、德语等。其中ISO8859-1是最著名的一个,定义了西欧拉丁语符号。注意,ISO8859-1与GB18030 在编码上也是重叠的。
很明显,iso8859-1编码表示的字符范围很窄,无法表示中文字符。但是,由于是单字节编码,和计算机最基础的表示单位一致,所以很多时候,仍旧使用iso8859-1编码来表示。而且在很多协议上,默认使用该编码。比如,虽然"中文"两个字不存在iso8859-1编码,以gb2312编码为例,应该是"d6d0cec4"两个字符,使用iso8859-1编码的时候则将它拆开为4个字节来表示:"d6d0 cec4"(事实上,在进行存储的时候,也是以字节为单位处理的)。而如果是UTF编码,则是6个字节"e4b8 ad e6 96 87"。很明显,这种表示方法还需要以另一种编码为基础。
中文汉字编码体系: