臵错误引
起的)。解决类似的字符 encoding 问题,需要了解 Jsp/Servlet 的运行过程,检查可能出
现问题的 各个点。
3.2 JSP/Servlet web 编程时的 encoding 问题
运行于Java 应用服务器的 JSP/Servlet 为 Browser 提供 HTML 内容,其过程如下图
所示:
其中有字符编码转换的地方有:
a.JSP 编译。Java 应用服务器将根据 JVM 的 file.encoding 值读取 JSP 源文件,并转
换为内部
字符编码进行 JSP 编译,生成 JAVA 源文件,根据 file.encoding 值写回文件系统。如果
当前系统语
言支持 GBK,那么这时候不会出现 encoding 问题。如果是英文的系统,如 LANG 是
en_US 的 Linux,
AIX 或 Solaris,则要将 JVM 的 file.encoding 值臵成 GBK 。系统语言如果是 GB2312,
则根据需要
,确定要不要设臵 file.encoding,将 file.encoding 设为 GBK 可以解决潜在的 GBK 字符
乱码问题 。
b.Java 需要被编译为 .class 才能在 JVM 中执行,这个过程存在与a.同样的
file.encoding 问
题。从这里开始 servlet 和 jsp 的运行就类似了,只不过 Servlet 的编译不是自动进行的。 c.Servlet 需要将 HTML 页面内容转换为 browser 可接受的 encoding 内容发送出去。
依赖于各
JAVA App Server 的实现方式,有的将查询 Browser 的 accept-charset 和 accept-language
参数或
以其它猜的方式确定 encoding 值,有的则不管。因此 constant-encoding 也许是最好的解决
方法。
对于中文网页,可在 JSP 或 Servlet 中设臵 contentType=\;如
果页面
中有GBK字符,则设臵为contentType=\,由于IE 和 Netscape对
GBK的支持程
度不一样,作这种设臵时需要测试一下。
因为16位 JAVA char在网络传送时高8位会被丢弃,也为了确保Servlet页面中的汉
字(包括内嵌的
和servlet运行过程中得到的)是期望的内码,可以用 PrintWriter ōut=res.getWriter() 取代 ServletOutputStream ōut=res.getOutputStream(), PrinterWriter 将根据contentType中指定的
charset作转换(ContentType需在此之前指定!);也可以用OutputStreamWriter封装
ServletOutputStream 类并用write(String)输出汉字字符串。
对于 JSP,JAVA Application Server 应当能够确保在这个阶段将嵌入的汉字正确传送出去。 d.这是 URL 字符 encoding 问题。如果通过 get/post 方式从 browser 返回的值中包含
汉字信息
, servlet 将无法得到正确的值。SUN的 J2SDK 中,HttpUtils.parseName 在解析参数时根
本没有考
虑 browser 的语言设臵,而是将得到的值按 byte 方式解析。这是网上讨论得最多的
encoding 问题
。因为这是设计缺陷,只能以 bin 方式重新解析得到的字符串;或者以 hack HttpUtils 类
的方式解
决。参考文章 2、3 均有介绍,不过最好将其中的中文 encoding GB2312、 CP1381 都改为
GBK,否则
遇到 GBK 汉字时,还是会有问题。
Servlet API 2.3 提供一个新的函数 HttpServeletRequest.setCharacterEncoding 用于在调
用
request.getParameter(“param_name”) 前指定应用程序希望的 encoding,这将有助于彻底解决
这个 问题。
WebSphere Application Server 对标准的 Servlet API 2.x 作了扩展,提供较好的多语言
支持。
上述c,d情况,WAS 都要查询 Browser 的语言设臵,在缺省状况下zh、zh-cn 等均被映射
为 JAVA
encoding CP1381(注意:CP1381 只是等同于 GB2312 的一个 codepage,没有 GBK 支持)。
这样做我
想是因为无法确认 Browser 运行的操作系统是支持GB2312, 还是 GBK,所以取其小。但
是实际的应用
系统还是要求页面中出现 GBK 汉字,最著名的是朱总理名字中的“?”(rong2 ,0xe946,
\镕),所
以有时还是需要将 Encoding/Charset 指定为 GBK。当然 WAS 中变更缺省的 encoding 没
有上面说的
那么麻烦,针对 a,b,参考文章 5 ),在 Application Server 的命令行参数中指定 - Dfile.encoding=GBK 即可; 针对 d,在 Application Server 的命令行参数中指定- Ddefault.client.encoding=GBK。如果指定了-Ddefault.client.encoding=GBK,那么c情况下可
以不再 指定charset。
3.3 数据库读写时的 encoding 问题
JSP/Servlet 编程中经常出现 encoding 问题的另一个地方是读写数据库中的数据。 流行的关系数据库系统都支持数据库 encoding,也就是说在创建数据库时可以指定它
自己的字符
集设臵,数据库的数据以指定的编码形式存储。当应用程序访问数据时,在入口和出口处都
会有
encoding 转换。对于中文数据,应当保证数据的完整性。GB2312,GBK,UTF-8 等都是可
选的数据库
encoding;如果选择 ISO8859-1(8-bit SBCS),那么应用程序在写数据之前须将 16Bit 的一
个汉字或
Unicode 拆分成两个 8-bit 的字符,读数据之后则需将两个字节合并起来,同时还有判别其
中的 SBCS
字符。没有充分利用数据库 encoding 的作用,反而增加了编程的复杂度,ISO8859-1不是
推荐的数据
库 encoding。JSP/Servlet编程时,可以先用数据库管理系统提供的功能检查其中的中文数据
是否正确 。
然后应当注意的是读出来的数据的 encoding,JAVA 程序中一般得到的是 Unicode。
写数据时则相
反。
3.4 定位问题时常用的技巧
定位中文encoding问题通常采用最笨的也是最有效的办法——在你认为有嫌疑的程序
处理后打印字
符串的内码。通过打印字符串的内码,你可以发现什么时候中文字符被转换成Unicode,什
么时候
Unicode被转回中文内码,什么时候一个中文字成了两个 Unicode 字符,什么时候中文字符
串被转成了
一串问号,什么时候中文字符串的高位被截掉了……
取用合适的样本字符串也有助于区分问题的类型。如:”aa啊aa?aa” 等中英相间、GB、
GBK特征
字符均有的字符串。一般来说,英文字符无论怎么转换或处理,都不会失真(如果遇到了,
可以尝试着
增加连续的英文字母长度)。
4.结束语
其实 JSP/Servlet 的中文encoding 并没有想像的那么复杂,虽然定位和解决问题没有定
规,各种
运行环境也各不尽然,但后面的原理是一样的。了解字符集的知识是解决字符问题的基础。
不过,随着
中文字符集的变化,不仅仅是 java 编程,中文信息处理中的问题还是会存在一段时间的。
5.参考文章
1) Character Problem Review
2) Java 编程技术中汉字问题的分析及解决
3) NLS Characters in WebSphere: SBCS/DBCS display on same page
4) GB18030
5) Setting language encoding in web applications: Websphere applications Server
作者简介
张建芳,软件工程师,毕业于北京理工大学计算机应用学院,有多年中文本地化经验。
您可通过
jfzhang@usa.net 与他联系。
/////////////////////////////////////////////////////////////////////////////////////
关于jsp乱码问题的解决。 1 最基本的乱码问题。