|
[size=0.76em]两个方向转换都有可能得到错误的结果:
- Unicode-->Byte, 如果目标代码集不存在对应的代码,则得到的结果是 0x3f.[size=1em]如:
"\u00d6\u00ec\u00e9\u0046\u00bb\u00f9".getBytes("GBK") 的结果是 "? ìé F? ù", Hex 值是 3fa8aca8a6463fa8b4.
[size=1em]仔细看一下上面的结果,你会发现 \u00ec 被转换为 0xa8ac, \u00e9 被转换为 \xa8a6... 它的实际有效位变长了! 这是因为 GB2312 符号区中的一些符号被映射到一些公共的符号编码,由于这些符号出现在 ISO-8859-1 或其它一些 SBCS 字符集中,故它们在 Unicode 中编码比较靠前,有一些其有效位只有 8 位,和汉字的编码重叠 ( 其实这种映射只是编码的映射,在显示时仔细不是一样的。Unicode 中的符号是单字节宽,汉字中的符号是双字节宽 ) . 在 Unicode\u00a0--\u00ff 之间这样的符号有 20 个。了解这个特征非常重要!由此就不难理解为什么 JAVA 编程中,汉字编码的错误结果中常常会出现一些乱码 ( 其实是符号字符 ), 而不全是'?'字符 , 就比如上面的例子。
- Byte-->Unicode, 如果 Byte 标识的字符在源代码集不存在,则得到的结果是 0xfffd.[size=1em]如:
Byte ba[] = {(byte)0x81,(byte)0x40,(byte)0xb0,(byte)0xa1}; new String(ba,"gb2312");
[size=1em]结果是"? 啊", hex 值是"\ufffd\u554a". 0x8140 是 GBK 字符,按 GB2312 转换表没有对应的值,取 \ufffd. ( 请注意:在显示该 uniCode 时,因为没有对应的本地字符,所以也适用上一种情况,显示为一个"?".)
[size=0.76em]实际编程中,JSP/Servlet 程序得到错误的汉字信息,往往是这两个过程的叠加,有时甚至是两个过程叠加后反复作用的结果 .
[size=0.76em]网上常出现的 JSP/Servlet encoding 问题一般都表现在 browser 或应用程序端,如 :
- 浏览器中看到的 Jsp/Servlet 页面中的汉字怎么都成了 ’ ? ’ ?
- 浏览器中看到的 Servlet 页面中的汉字怎么都成了乱码?
- JAVA 应用程序界面中的汉字怎么都成了方块?
- Jsp/Servlet 页面无法显示 GBK 汉字。
- JSP 页面中内嵌在 <%...%>,<%=...%> 等 Tag 包含的 JAVA code 中的中文成了乱码,但页面的其它汉字是对的。
- Jsp/Servlet 不能接收 form 提交的汉字。
- JSP/Servlet 数据库读写无法获得正确的内容。
[size=0.76em]隐藏在这些问题后面的是各种错误的字符转换和处理(除第 3 个外,是因为 Java font 设置错误引起的)。解决类似的字符 encoding 问题,需要了解 Jsp/Servlet 的运行过程,检查可能出现问题的各个点。
[size=0.76em]其中有字符编码转换的地方有 :
- [size=1em]JSP 编译。Java 应用服务器将根据 JVM 的 file.encoding 值读取 JSP 源文件,编译生成 JAVA 源文件,再根据 file.encoding 值写回文件系统。如果当前系统语言支持 GBK,那么这时候不会出现 encoding 问题。如果是英文的系统,如 LANG 是 en_US 的 Linux, AIX 或 Solaris,则要将 JVM 的 file.encoding 值置成 GBK 。系统语言如果是 GB2312,则根据需要,确定要不要设置 file.encoding,将 file.encoding 设为 GBK 可以解决潜在的 GBK 字符乱码问题
- [size=1em]Java 需要被编译为 .class 才能在 JVM 中执行,这个过程存在与 a. 同样的 file.encoding 问题。从这里开始 servlet 和 jsp 的运行就类似了,只不过 Servlet 的编译不是自动进行的。对于 JSP 程序 , 对产生的 JAVA 中间文件的编译是自动进行的 ( 在程序中直接调用 sun.tools.javac.Main 类 ). 因此如果在这一步出现问题的话 , 也要检查 encoding 和 OS 的语言环境,或者将内嵌在 JSP JAVA Code 中的静态汉字转为 Unicode, 要么静态文本输出不要放在 JAVA code 中。 对于 Servlet, javac 编译时手工指定 -encoding 参数就可以了。
- [size=1em]Servlet 需要将 HTML 页面内容转换为 browser 可接受的 encoding 内容发送出去。依赖于各 JAVA App Server 的实现方式,有的将查询 Browser 的 accept-charset 和 accept-language 参数或以其它猜的方式确定 encoding 值,有的则不管。因此采用固定 encoding 也许是最好的解决方法。对于中文网页,可在 JSP 或 Servlet 中设置 contentType="text/html; charset=GB2312";如果页面中有 GBK 字符,则设置为 contentType="text/html; charset=GBK",由于 IE 和 Netscape 对 GBK 的支持程度不一样,作这种设置时需要测试一下。 因为 16 位 JAVA char 在网络传送时高 8 位会被丢弃,也为了确保 Servlet 页面中的汉字(包括内嵌的和 servlet 运行过程中得到的)是期望的内码,可以用 PrintWriter out=res.getWriter() 取代 ServletOutputStream out=res.getOutputStream(). PrinterWriter 将根据 contentType 中指定的 charset 作转换 (ContentType 需在此之前指定! ); 也可以用 OutputStreamWriter 封装 ServletOutputStream 类并用 write(String) 输出汉字字符串。 对于 JSP,JAVA Application Server 应当能够确保在这个阶段将嵌入的汉字正确传送出去。
- [size=1em]这是解释 URL 字符 encoding 问题。如果通过 get/post 方式从 browser 返回的参数值中包含汉字信息, servlet 将无法得到正确的值。SUN 的 J2SDK 中,HttpUtils.parseName 在解析参数时根本没有考虑 browser 的语言设置,而是将得到的值按 byte 方式解析。这是网上讨论得最多的 encoding 问题。因为这是设计缺陷,只能以 bin 方式重新解析得到的字符串;或者以 hack HttpUtils 类的方式解决。参考文章 2 均有介绍,不过最好将其中的中文 encoding GB2312、 CP1381 都改为 GBK,否则遇到 GBK 汉字时,还是会有问题。 Servlet API 2.3 提供一个新的函数 HttpServeletRequest.setCharacterEncoding 用于在调用 request.getParameter(“param_name”) 前指定应用程序希望的 encoding,这将有助于彻底解决这个问题。
|
|