Java中的字符编码

在日常开发中,经常会遇到关于编码的问题,让人总有种弄不清的感觉,这次我就整理一下,以便自己复习和大家分享,文中如有错误请及时指正。

ASCII编码

先说说编码的由来,它的由来再简单不过了,为了让计算机存储字符。大家知道计算机底层的处理只有二进制,也就是0和1。在没有计算机的时候,人们使 用8个晶体管来表示状态和动作,怎么表示呢?也就是说,每个晶体管可以亮或者灭,也就是0和1,根据8个晶体管不同的亮灭组合来表示不同的意思。那么根据 数学知识可以知道,一共有256种组合方式(2的8次方)。

这256种表示一开始并没有全部被使用完,只使用了一部分(32个,表示各种状态),随着计算机的出现和发展,很快剩下的位置也开始被占用,用来表 示空格、标点符号、数字、大小写字符等,一共使用了127个位置,这127个位置所组成的字符对应表,就称为ANSII编码表(American Standard Code for Information Interchange,美国信息互换标准代码)。简单来说,ANSII是最原始的计算机编码表,表示了西方人使用的几乎所有的常用字符。

扩展字符集

扩展字符集还是ASCII编码表中的,也就是256-127剩下的哪些位置,这些位置存储了一些更加有意思的字符,比如横线、竖线交叉等等,也就是扩展后的ASCII码表

GB2312编码表

很明显,随着世界上越来越多的人使用计算机,而原本的ASCII编码表已经没有位置了,那么中国就推出了自己的编码表,将6000多个常用汉字编了 进去。不过对ASCII编码表做了一些修改:小于127的字符意义与ANSII编码表一样,把扩展字符集全部舍弃,两个大于127的字符连在一起时,就表 示一个汉字。这样就可以组合出7000多个汉字,并且还加入了数学符号、罗马希腊的字母、日文等等,这张码表,就是GB2312编码表。简单来 说,GB2312编码表就是ANSII编码的中文扩展码表。

GBK编码

不过很快,这张GB2312编码表也不够用了,毕竟只有7000多个汉字,然后就继续扩展,加入了20000多个汉字和符号,就形成了GBK编码表,GBK包含GB2312的所有编码

GB18030

接着,少数民族的文字也加入了进来,就形成了GB18030编码表

DBCS(Double Byte Charecter Set 双字节字符集)

以上关于中文的这些编码表,被统称为DBCS,在DBCS系列标准里,最大的特点是两字节长的汉字字符和一字节长的英文字符并存于同一套编码方案 里,因此程序为了支持中文处理,必须要注意字串里的每一个字节的值,如果这个值是大于127的,那么就认为一个双字节字符集里的字符出现了,占两个字节, 而小于127的,占一个字节。

Unicode

类似中国,全世界的各个国家都开始编写自己语言的码表,这样就形成了一个混乱的编码时代,于是乎,就有人要解决这个问题,这个组织就是ISO (国际标谁化组织)。他们的办法很简单,废除所有现存编码,重新编纂了一套编码,这套编码包含全世界所有的字符,这就是Unicode编码表的由来。为了 兼容各种字符,规定,Unicode编码中不论中文英文还是别的什么文,每个字符占用两个字节。但是问题也来了,在制定Unicode编码的时候,没有考 虑到跟已有任何一种编码兼容,这使得同一个汉字可能在不同码表表示的不一样,也就是乱码问题的由来。没有一种简单的算术方法可以把文本内容从 UNICODE编码和另一种编码进行转换,这种转换必须通过查表来进行。

如前所述,UNICODE 是用两个字节来表示为一个字符,他总共可以组合出65535不同的字符,这大概已经可以覆盖世界上所有文化的符号。如果还不够也没有关系,ISO已经准备 了UCS-4方案,说简单了就是四个字节来表示一个字符,这样我们就可以组合出21亿个不同的字符出来。

UTF-8

什么是UTF-8呢?简单来说,UTF-8是一种Unicode编码表的存储方式和传输标准。它是为了解决网络传输Unicode编码而诞生的。来看一张UTF-8实现Unicode存储的对照表:
Java中的字符编码_第1张图片
从图中看出,UTF-8编码实现所占用的字节数是不确定的,1到6位都有可能。其实4个字节就几乎可以表示全世界的字符了,所以5个字节和6个字节的那种表示方法被停用了。

而且ISO也为以后做了准备了,指定了UCS-4方案,说简单了就是四个字节来表示一个字符,这样我们就可以组合出21亿个不同的字符出来。

中文到底占几个字节?

这个问题经常被问到,我是这样理解的,这个答案要分情况。如果是DBCS那一套编码,那么中文占两个字节,如果是UTF-8编码,中文占3个字节或 4个字节,至于到底是3个还是4个,要看你给的中文在上面那张图的哪个区间段中,不同的区间段对应的UTF-8格式不一样,导致了结果的不一致。

到这里就差不多了,至于著名的“联通”乱码问题我这里不多说了,开发平时也用不上,有兴趣的可以度娘。

说了这么多,可能有些混淆了,这里有个问题,Unicode规定每个字符占两个字节,那么UTF-8编码中算几个呢,中文、英文都分别在不同编码下占几个字节?

关于这个问题,还是那句话,要分情况,下面用程序来验证,这是最清楚的方式了:

1 String testStr = "abc";
2 String testCh = "你";
3 System.out.println("GBK编码:英文长度" + testStr.getBytes("GBK").length);
4 System.out.println("GBK编码:中文长度" + testCh.getBytes("GBK").length);
5 System.out.println("UTF-8编码:英文长度" + testStr.getBytes("UTF-8").length);
6 System.out.println("UTF-8编码:中文长度" + testCh.getBytes("UTF-8").length);
7 System.out.println("Unicode编码:英文长度" + testStr.getBytes("Unicode").length);
8 System.out.println("Unicode编码:中文长度" + testCh.getBytes("Unicode").length);

以下为输入结果:
Java中的字符编码_第2张图片
这 个结果GBK与UTF-8的没有疑问,Unicode这个结果有点意外,为什么"abc"三个字符会占8位呢?这个是因为如果你指定要用Unicode来 编码,Java其实使用的是UTF-16LE来处理,简单来说,就是这是UTF-16编码的结果,那么为什么是8位呢?因为在UTF-16中,不管是中英 日美还是什么文,每个字符都占两个字节,那么"abc"就是6个字节,而为了通信,UTF-16需要在字符前面加标识,这个标识叫BOM头,这个不用管, 只要知道,要加2个字节,所以6 + 2 = 8,就是8个字节,而中文"你"占2个字节,再加上BOM头,就是4个字节。那么就记住,UTF-16中,每个字符占两个字节,总的字符长度 = 字符个数 * 2 + 2,就可以了。比如中文"你好”,在UTF-16编码下占6个字节。

总结一下:

  • 一个英文字符在DBCS编码体系中,占一个字节,在UTF-8中占1个字节,在UTF-16中占2个字节,但是在UTF-16计算字节数时,要 注意,是总的字符数的两倍,再加上BOM头的两个字节。在Unicode表中,占两个字节。Java是基于Unicode编码的,所以其实Java中的字 符都是两个字节,但是由于编码实现不一样,如UTF-8,所以要分清楚这几种情况。

  • 一个中文字符在DBCS编码体系总,占两个字节,在UTF-8中占3到4个字节(中文在Unicode表中对应的数字从3个字节的格式开始),在UTF-16中占2个字节,同样在UTF-16计算字节数时,要注意,是总的字符数的两倍,再加上BOM头的两个字节。

                分享到:


你可能感兴趣的:(Java中的字符编码)