前言
计算机中的数据是以二进制格式表示的(其中8位二进制称为一个字节,比如00100101就是一个字节。通常为了更方便的表示二进制数据,也可以转换成16进制表示出来,比如00100101就可以用0x25来表示)。把所有字符转换成二进制数据的规则就是字符编码。字符编码的方式很多,本文对每一种字符编码做尽可能详细的讲解。
本文中红色字体或加粗字体是需要重点理解或者记忆的。
1. ASCII码
ASCII是美国国家标准定制的一套基于拉丁字母的电脑编码系统,可表示数字、字母等字符符号。
一个ASCII码在计算机中由一个字节存储,因此它最多可表示256个符号(一个字节为8位,2的8次方等于256),实事上,标准的ASCII编码时只用到了低7位(最高位统一为0,或者为奇偶校验位),故ASCII码可表示的数据一共只有128个(2的7次方)。这128个字符中,其中95个为可显示字符(可打印字符,比如数字、字母、标点符号),还有33个如比如“换行”之类的控制字符(控制字符主要是用来操控已经处理过的文字)。
对于ASCII具体哪个编码表示哪个符号,大家可以去查阅ASCII编码码表,并具最好能记住数字符、字母、回车换行等常用字符对应的ASCII编码,至少记住它的十进制编码。
常用字符对应的ASCII编码:
回车,ASCII码13(十进制,下同)
换行,ASCII码10
空格,ASCII码32
数字0到9,ASCII依次是48到57
大写字母A到Z,ASCII依次是65到90
小写字母a到z,ASCII依次是97到122
大小规则总结:
1)数字0~9比字母要小。如"7"<"F";
2)数字0比数字9要小,并按0到9顺序递增。如"3"<"8"
3)字母A比字母Z要小,并按A到Z顺序递增。如"A"<"Z"
4)同个字母的大写字母比小写字母要小。如"A"<"a"。
2. iso8859-1码
iso8859-1通常叫做Latin-1,它和ascii编码相似,都属于单字节编码,不同于ASCII的是,每个字节中的最高位也参与了编码(如果最高位为0,它的意义同ASCII)。正因为如此,iso8859-1最多能表示的字符范围是0-255,应用于英文系列。很明显,iso8859-1编码表示的字符范围很窄,无法编码中文字符。尽管如此,我们可以先把中文字符按照其它的编码方式编码成二进制数据,然后将编码后的结果再逐字节逐字节的用iso8859-1解码。也就是说,中文字符,它没有iso8859-1编码,但可以在用其它编码方式编码后的基础上再用iso8859-1编码来表示。举个栗子,虽然“中文”的“中”这个字不存在iso8859-1编码,可以先把它按gb2312编码方式编码为"d6d0"这个二字节的编码(16进制),然后将它拆开为两个字节("d6" 与 "d0"),每个字节都可以看作是一个iso8859-1码。
iso8859-1编码在网络传输中的利用:
iso8859-1由于是单字节编码,和计算机最基础的表示单位一致,因此在很多网络传输协议上,默认使用iso8859-1编码。网络上传输的数据都是二进制的,服务器从网络io流中收到这些数据后,默认把每个字节的数据按iso8859-1编码来处理。
实事上,通过网络流传输中文时,客户端可以先把中文字符按照其它的编码方式(比如GBK或UTF-8)转换成字节数据发送到服务器,服务器收到后在不指定编码方式的情况下默认会逐个逐个字节的按照iso8859-1编码方式来解码。
写点题外话,对于java web程序员来说,有时候可以在servlet代码中看到类似于String str = new String(restr.getBytes("iso8859-1"),"utf-8");的转换代码,它的意思就是先把字符串restr通过restr.getBytes("iso8859-1")编码为字节数组(restr是请求参数被按照iso8859-1方式解码后的结果。如果请求参数为中文,因为iso8859-1编码无法直接解码中文,会导致解码后的restr为乱码),然后再按照utf-8的编码方式重新转换为一个新的字符串。
当然,在java web中也可以用request.setCharacterEncoding("utf-8");方法,直接告诉服务器按照指定的utf-8方式编码。但值得一提的是在执行setCharacterEncoding()之前,不能执行任何getParameter(),而且,该指定只对POST方法有效,对GET方法无效。原因是在执行第一个getParameter()的时候,java将会按照编码分析所有的提交内容,而后续的getParameter()不再进行分析,所以setCharacterEncoding()无效。而对于GET方法提交表单是,提交的内容在URL中,一开始就已经按照编码分析所有的提交内容,setCharacterEncoding()自然就无效。多说一句,为了避免总是要写setCharacterEncoding("utf-8"),通常会在web.xml配置文件中配置一个最上层的字符编码过滤器。
3. GB2312码
无论是ascii码还是iso8859-1码,都无法直接编码中文,于是有了GB2312码。GB2312 是对 ASCII 的中文扩展,考虑到每个ASCII码只用了一个字节的底7位(高位为0),所以每个的ASCII码都小于或等于127(01111111)。于是规定:一个小于127的字符的意义与原来相同(为了兼容ASCII码),但两个大于127的字符连在一起时,就表示一个汉字,而且这两个字节中,前面的一个字节(他称之为高字节)从0xA1用到0xF7,后面一个字节(低字节)从0xA1到0xFE,这样我们可以组合出大约7000多个简体汉字,这就是GB2312编码。
这样,计算机在解码时,可以逐字节逐字节的判断它的是否小于127,如果小于或等于127,就按ASCII直接解码,如果大于127,就再往后多读取一个字节,如果后面那个字节也大于127,就把这两个字节连起来再通过查找GB2312编码码表来解码成一个中文字符。由此可见,GB2312码是一种变长的编码方式,英文占一个字节(而且小于127),中文占两个字节(都是大于127的字节),而且兼容ASCII码。
4. GBK码
GB2312码中用两个连续的大于127字节的数据来表示一个中文,它能表示的数量刚好也就满足简体中文编码的需要。后来发现,其实表示一个中文的两个字符中,可以不要求两个字节都大于127,只要第一个字符大于127,就可以作为解码中文的开始字符,第二个字符是否大于127都不会对计算机解码造成二义性错误(在逐字节解码的过程中,只要读到一个节字大于127,就直接往后再读一个字节,不判断第二个字节是否大于127就直接将这两个字节连到一起然后再通过编码码表来解码)。这样,在GB2312码的基础上,通过不限定第二个字节是否大于127的方式扩展出了GBK码。GBK包括了GB2312的所有内容,同时又增加了近20000个新的汉字(包括繁体字)和符号。
5. GB18030码
在GBK码的基础上,再进一步扩展,增加了一些连续四个字节的汉字,于是也就有了GB18030码,GB18030码完全兼容GBK码,而且增加了少数民族文字。
综上所述,以上能表示中文的字符编码中,可编码的范围从小到大依次是:ASCII < GB2312 < GBK < GB18030,而且它们后者兼容前者,其中GB2312和GBK用两个字节表示中文,GB18030有些用两个字节表示一个汉字,有些有两个字节表示汉字。
6.UNICODE编码
因为每个国家都搞出像天朝这样一套自己的编码标准,在跨国跨语言使用时存在储多不便。为了统一,ISO(国际标谁化组织)重新搞了一套标准,也就是UNICODE编码。
UNICODE这是最统一的定长编码,有双字节编码(UCS-2)和四字节编码(UCS-4,备用)两种。其中UCS-2包括英文字母在内,只能表示65535个字符,IOS预备的UCS-4方案,可以组合出21亿个不同的字符出来(最高位有其他用途)。
其实,英文字母只用一个字节表示就够了,但是其他更大的符号可能需要3个字节或者4个字节,甚至更多。而Unicode统一规定,每个符号用两个或四个字节表示,那么每个英文字母前都必然有二到三个字节是0,这对于存储来说其实是极大的浪费。
而且它不兼容iso8859-1编码,也不兼容gb2312、gbk、gb18030等任何编码。不过,相对于iso8859-1编码来说,uniocode编码只是在前面增加了一个0字节,比如字母a为"00 61"。
但是UNICODE这种定长编码便于计算机处理(注意GB2312/GBK不是定长编码),而unicode又可以用来表示所有字符,所以在很多软件内部是使用unicode编码来处理的,比如java。
需要注意的是,Unicode只是一个符号集,它只规定了符号的二进制代码,却没有规定这个二进制代码应该如何存储。于是出现了多种存储方式,也就是说有许多种不同的二进制格式,可以用来表示unicode。unicode在很长一段时间内无法推广,直到互联网的出现。
7.UTF
考虑到unicode编码不兼容iso8859-1编码,而且容易占用更多的空间:因为对于英文字母,unicode也需要两个字节来表示。所以unicode不便于传输和存储。因此而产生了utf编码,utf编码兼容iso8859-1编码,同时也可以用来表示所有语言的字符,不过,utf编码是不定长编码,每一个字符的长度从1-6个字节不等。另外,utf编码自带简单的校验功能。
UTF-8是Unicode的实现方式之一,一般来讲,对于UTF-8,英文字母都是用一个字节表示,而汉字使用三个字节。另外,还有UTF-16(字符用两个字节或四个字节表示),UTF-32(字符用四个字节表示)。
顾名思义,UTF-8就是每次8个位传输数据,而UTF-16就是每次16个位。UTF-8就是在互联网上使用最广的一种unicode的实现方式,这是为传输而设计的编码,并使编码无国界,这样就可以显示全世界上所有文化的字符了。
UTF-8的编码规则很简单,只有二条:
(1)对于单字节的符号,字节的第一位设为0,后面7位为这个符号的unicode码。因此对于英语字母,UTF-8编码和ASCII码是相同的。
(2)对于n字节的符号(n>1),第一个字节的前n位都设为1,第n+1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的unicode码。
综上所述,UTF-8最大的一个特点,就是它是一种变长的编码方式。它可以使用1~4个字节表示一个符号,根据不同的符号而变化字节长度。而且UTF-8编码的文件比GB2312更占空间大。
以上就是本人以常见字符编码的汇总,正文中本文中红色字体或加粗字体是需要重点理解或者记忆的。想了解更多信息,请关注本人公众号