Posted on 2008-10-08 15:07
英雄 阅读(2466)
评论(0) 编辑 收藏
真是一个老生常谈的问题啦!
项目上遇到啦,环境是:
1.中间件和数据库都各自在hp-unix机器上。数据库oracle字符集采用ZHS16GBK;中间件(基于jdk1.5)启动时指定字符集是#export lang=zh_CN.hp15CN。
2.客户端采用applet实现。
情况就是有个人名“李袆”,通过客户端输入保存后,再查询出来就变成了“李?”。
好啦,重新复习一下java乱码问题,google了一大把,有的说得都不对,现在先推荐两篇权威的。
http://java.sun.com/developer/technicalArticles/Intl/HTTPCharset/;
http://java.sun.com/developer/technicalArticles/Intl/Supplementary/;
开始解决问题啦。
1.jvm在内存里存字符串是以utf-16存的,但是class文件和序列化对象文件里的字符串内容以“modified”utf-8存;
在jvm1.5里char代表的是编码unit,不是编码point。这样就有可能两个char才代表一个字符。这个“代表”的变化是为适应unicode从1.0升到2.0版本,老jdk到新jdk的一个概念改变,很多api和jdk工具因此也发生变化。那时unicode1.0里就是定长16位代表字符,所以老jdk里一个char就代表一个point,当然也是一个unit。但是unicode2.0扩展开来了,因此为了兼容,新jdk就确定一个char只代表一个unit,表示一个point得用int。
“modified”utf-8是java对标准utf-8编码的一点更改,可以认为是种特殊编码,为了适应java需要而存在的字符集。
2.客户端接受输入法输入后,在客户端jvm里以utf-16存“袆”,而后发送给中间件是通过序列化,以"modifed"utf-8传送,中间件反序列化就又在中间件jvm里以utf-16存“袆”啦。这时中间件里的那个java对象要通过 jdbc给oracle发送过去此字符串,oracle的jdbc驱动将以系统默认字符集进行编码传送。可是zh_CN.hp15CN是gb2312标准,没有这个生僻字,只好搞一个“不存在编码”编码,这一编码就把信息丢失了;而数据库是以ZHS16GBK认识接受到数据,将这个“损坏的字”存到数据文件里啦。这里请问下同行读者,oracle数据库端会不会做zh_CN.hp15CN到ZHS16GBK的转码还是直接认为传过来的一定是ZHS16GBK直接存到数据文件?我个人认为数据库不做这种转码,浪费性能么。但不管怎么说,在中间件哪里,“袆”字就被“损坏”了,传到数据库怎么样也无法恢复了。
3.这样客户端再去获取时肯定是显示不出来“袆”字啦。
于是让客户执行#export lang=zh_CN.gb18030再启动中间件,问题解决。
补充:
1.oracle的jdbc thin驱动就是这样,使用系统默认字符集进行传送编码,不像mysql可以在连接url中指定。不过我个人认为这也不错,因为你即使在url指定正确的编码格式,还要保证操作系统装着这个字符集呢。
2.jsp
%@page contentType="text/html;charset=gb2312" language="java" pageEncoding="gb2312"%这个标记分两部分,pageEncoding是指jsp文本文件本身存在硬盘的编码格式,servlet容器按这个编码格式读取,从而再做转码到合适编码(一般utf-8)的java文件,再编译成含“modified utf-8字符串”的class文件,再加载成实例对象;当该对象吐出response时,以contentType里的charset进行编码在网上传送给客户端,并置response头信息编码格式。
3.浏览器客户端接受到response时,会根据头信息获取编码格式,从而解析出正确字符,再根据html规范进行显示。而对于网页提交表单时,也会根据这个信息进行表单内容数据的编码。编码后再进行url编码即发送给服务器。可是表单输入有可能超出这个字符集,则多数浏览器会采用(unicode point number)来编码这个字符,继续发送给服务器。同样,如果浏览器在接受response时,得到这种形式的字符串,多数也将进行解码从而显示出来。
4.多数servlet容器在解析request获取参数时统一采用默认配置的iso-8859-1。这个字符集只包含西欧字符,因此解析前统一setCharacterEncoding是必须的。 当然多数都也可以修改默认配置啦。