您的位置:首页 > 编程语言 > Java开发

java字符编码原理解析

2010-09-27 14:43 961 查看
可以理解为计算机没有字符的概念,只有字节。字符是存在于人类语言层的概念,其作用是为了人与人之间的交流,因为字节对于人类是不可读的,但是计算机存储所有的数据都是按照字节存储。
因此要将人类意识中的字符存储到计算机中,则必须将字符转换为字节数据,那么怎么转化呢,则必须要一种映射规则,这里的映射规则就是通常意义中的字符编码,比如说该文件是GBK编码,可以说为:这个文档中的字符数据是按照GBK这种字符字节映射规则将字符转换为字节存储的。

所以所有要将人类意识中的字符存储在计算机或者需要通过计算机传递时,都涉及到字符和字节之间的通过某种映射规则的转换。

将字符按照映射规则转化为字节称为编码,反之称为解码。

弄明白了字符和字节,以及为什么要编码解码的意义,在看在java中哪些地方需要编码:

1:java 的源代码文件.我们在使用编辑器编辑数据后,需要将编辑器中的”字符们“存储起来时,需要选择一种映射规则存储在计算机中。当编译java源代码时,javac读取源代码文件,得到源代码文件的字节数据后,需要将其转换为字符,那么就必须按照刚刚存储源代码时选择的映射规则同样的
映射规则才能将这些字节正确还原为人类意识中的字符,然后再将这些字符使用utf-16映射规则映射为字节存储在.class中。所以在编译java源代码时,必须指定源代码的字符字节映射规则(编码),如果指定错了,那么映射回来的字符就会出错。
注:javac默认采用本地编译平台的编码读取源代码文件。所以如果开发团队中有些人是日文系统,有些是中文系统,但是又没有统一源代码编码,上传到cvs后,后来又在utf-8上编译,呵呵,全乱了。

2: 控制台的编码:当我们在java代码中使用System.out.println();输出字符时,向操作系统传递数据,让操作系统再显示出来。这中间也存在编码。记住:所有涉及字符的地方都涉及编码。因为底层都是通过字节传递的,要通过字节传递就必须选择一种字符字节映射规则。
试想在java端我们采用一种映射方式将字符映射为字节,将这些字节发送给操作系统,操作系统得到字节数据,然后再使用某种映射方式将字节映射回字符。如果采用的映射方式不对也就会产生显示出不可预测的数据,不是用户希望的结果,这就是乱码
而java实现的时候是采用的本地操作系统默认的映射方式将字符映射为字节,操作系统也是采用默认的将字节映射回字符,因此这个过程中是不会出现任何错误的。那么我们在控制台看见的乱码是因为在java内存中还是字符时这个时候这个字符就已经不是你所希望看见的了。
如上面说的源代码编码,如果编译的时候指定编码错误,那么将源代码字节映射为字符就已经出错,然后将错误的字符采用utf-16映射为了字节,运行的时候再将这些字节映射为字符,这个时候的字符就已经是错的了(这个字符是本来是utf-16映射规则映射成的字节但是后来又按照gbk映射
规则映射成的字符。和控制台出现乱码容易混淆的情况是远程控制的时候如使用ssh,因为这个时候又多了一层ssh服务端向ssh客户端发送将字符按照某种映射规则映射成的字节数据和ssh客户端选取一种映射规则将字节映射为字符的情况。

3:JSP 文件的编码。在JSP中使用pageEncoding指定jsp源文件的编码。原理和上面第一条中的java源代码一样。但是为什么jsp需要而.java文件不需要呢。因为jsp是在服务器上编译的。你在本地机上写jsp文件,存储的为默认编码GBK,到服务器上以后万一服务器为utf-8编码,如果采用和.java一样的策略不是就会出错了吗。

4:web服务端向浏览器发送数据:因为要通过网络传递字符数据,所以需要将字符按照某种方式映射为字节,在浏览器端,浏览器接收到字节数据再选取某种映射方式反映射为字符供用户观看。同样,如果选取的影射方式不匹配就会出错。所以可以让服务器将编码方式告诉客户端。在HTTP的报
文头中可以包含这些信息(如Content-Type: text/html;charset=GBK),这可以通过response.setContentType实现(在jsp中使用<%@ page contentType=""%>指令将转化为response.setContentType代码)。如果在jsp中指定了 pageEncoding但是没有指定contentType,生成的servlet代码中默认使用pageEncoding的编码设置 contentType.如果没有设定contentType服务器(tomcat)默认设定为iso-8859-1。

5:浏览器向服务器发送的数据(和浏览器以及浏览器的配置相关):同样也要通过网络传输。所以需要知道客户端采用的是什么映射规则将用户输入的字符映射为字节传递给服务端. 客户端一般采用的是该页面的编码发送数据(这个我没有认真测试过),但是web服务器(tomcat)确是默认采用iso-8859-1映射规则,所以双方的映射规则不一致。解决方案就是
request.setCharacterEncoding设置服务器针对浏览器发送来的字节数据的映射规则 (不同的服务器该方法实现策略可能不一样,在项目中遇到过weblogic和tomcat不一样,weblogic该方法会将HTTP头和内容块数据都采用该方法的参数设置映射规则,但是在tomcat中该映射规则只用于内容块,而报文头的映射规则在server.xml的connector的 URIEncoding配置).
不同的浏览器可能也存在不同。我在IE6中测试服务器发送utf-8的编码,通过抓包发现服务器发送下来的数据的确是utf-8的,但是如果通过url传递如<a href="1.jsp?a=趁"/>结果发现IE传递的是e8,b6(少了一个字节),而"趁"的utf-8编码是e8,b6,81。但是如果通过form提交,不管是用get/post方法,传递的都是"%e8,%b6,%81"
这种以“%”开始的编码是将二进制使用十六进制表示。但是在opera中,上面的三种方法都传递的是"%e8,%b6,%81".那么如果服务器发送的是gbk编码呢?呵呵自己测试试

6:javascript 编码:javascript和浏览器也有关系,IE中,如使用XMLHTTPRequest的open方法调用open("GET", "2.jsp?a=人");虽然当前页面是utf-8编码,但是javascript传递的是c8cb,这是"人"的gbk编码。而在opera中传递的是%E4%BA%BA,是正确的.如果服务器发送的字节是utf-16编码,IE中仍然传递的是"人"的gbk编码,opera也仍然传递的是utf-8 的编码。
不知道浏览器是不是可以在哪儿配置

7:数据库编码:数据库不好测试,因为还涉及到数据库客户端编码的问题。如果将正确的字符按照数据库指定的编码存储在数据库文件中,当查询数据时,数据库服务器将正确的字符发送给了客户端,但是客户端字符字节映射规则如果设置错误,可能会导致用户错误地认为数据库中存储的是错误的字符。最好的办法是抓包分析,但是数据库协议非常复杂,又无法抓包,
但是如果在某个过程中发生了出错,原理和上面描述的一样,一定是浏览器<->web服务器,web服务器<->数据库,数据库<->数据库客户端,ssh客户端<->ssh服务端等等之间某个地方出现了问题。

要解决双方选择的字符字节映射规则不一致而导致的乱码问题:
1:是告诉对方自己采用的什么映射规则将字符转换为字节的
2:用协议约定,约定双方都用某个规则映射
3:类似于XML在第一行标志下面的数据是采用什么映射规则映射的
4:在文件头存储特殊字节标志映射规则,如windows处理utf-16(会导致如果"联通"两个字出现在文件开始的话出现问题,大家可以到网上搜索一下这个情况,分析分析,呵呵)

附件:抓包工具下载http://www.wiresharkbook.com/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: