原文如下:
>>>>>>>
......前不久,我的一位技术上的朋友发信给我说,他终于找到了
Java Servlet 中文问题的根源。两周以来,他一直为 Java Servlet
的中文问题所困扰,因为每面对一个含有中文字符的字符串都必须进行强制转换才能够得到正确的结果(这好象是大家公认的唯一的解决办法)。后来,他确实不想如此继续安分下去了,因为这样的事情确实不应该是高级程序员所要做的工作,他就找出
Servlet 解码的源代码进行分析,因为他怀疑问题就出在解码这部分。经过四个小时的奋斗,他终于找到了问题的根源所在。原来他的怀疑是正确的, Servlet
的解码部分完全没有考虑双字节,直接把 %XX 当作一个字符。(原来 Java Soft 也会犯这么低级的错误!)
如果你对这个问题有兴趣或者遇到了同样的烦恼的话,你可以按照他的步骤对 Servlet.jar 进行修改:
找到源代码 HttpUtils
中的 static private String parseName ,在返回前将 sb(StringBuffer) 复制成 byte bs[] ,然后
return new String(bs,”GB2312”)。作上述修改后就需要自己解码了:
HashTable form=HttpUtils
.parseQueryString(request.getQueryString())或者
form=HttpUtils.parsePostData(……)
千万别忘了编译后放到
Servlet.jar 里面。
......
<<<<<<<<<
请问“高级”程序员几个问题:
1 如果这是一个商业产品的话,难道客户需要你Hacking过的Servlet.jar才运行这个应用吗?
2
难道这个产品只能用在中文GB2312上吗?如果是日文应用怎么办,如法Hacking吗?
也许我错了,但我的感觉是犯低级错误的不是JAVA
SOFT,因为JAVA应用的本地化不是在WEB应用这一层实现的,而是JVM的系统缺省编码方式根据操作系统的环境设置(locale)改变来实现。在文章发表在2000年年底,当时的LINUX对中文的locale支持还有限,因此,在LINUX上不能根据locale的设置将系统缺省的编码方式变成GB2312,从而改变JVM缺省的编码方式。
关于LINUX对l10n的支持请看:Linux程序员必读:中文化与GB18030标准
如何设置可以让LINUX从系统层次就支持中文编码呢?
所以在redhat6.x下,无论你怎么设置locale,系统缺省的缺省file.encoding都是ISO_8859_1 因为redhat6.2是基于
glibc-2.1.x的。在redhat7.x
系统内核所基于的glibc-2.2.x对l10n有了更完整的支持,所以可以通过设置
LC_ALL=zh_CN.GB2312;export
LC_ALL
LANG=zh_CN.GB2312;export LANG
让系统缺省的编码方式变成GB2312
GBK...从而改变JVM的缺省编码方式(file.encoding),之后,任何字节流到字符流的转换,JVM都会按照系统缺省编码方式进行转换。
在基于glibc2.2以上的linux上:是可以通过locale的设置来改变系统缺省的编码方式,从而改变应用的缺省编码、解码方式的。
这里有2点我想说明:
据我所理解的范围内,JDK1.3中非常不符合JAVA的国际化规范的是在使用URLEncoder的时候:
比如在中文WIN98上运行的应用,使用URLEncoder.encode(String
s)时:比如“中文”这2个字直接被Encoding的话结果是"%3F%3F"=>"??"。原因很简单,“中文”在encode()过程中需要先按GBK编码方式编码成4个BYTE后再URLEncoding才是正确的。这个在JDK1.4中也修正了。方法encode(String
s)已经不鼓励使用,取而代之的是除了需要进行URLEncoding的字符串外,同时需要指定字符串编码方式的encode(String s, String
enc)。这样,URLEncoder就可以和系统缺省的编码方式无关了。
在JDK1.3下,一个基于web-app框架的应用中,这个问题可以通过在WEB-INF/web.xml中设置来解决:
<web-app
character-encoding="your_system_default_file.encoding">
...
</web-app>
如果产品是在中文WINDOWS98运行,缺省字符集是用GBK,则这个应用的web.xml需要设置成:
<web-app
character-encoding="GBK">
...
</web-app>
以上2个方法仍然只是让应用更方便地本地化了,而应用本身并不是真正的国际化应用。设想一下如何设计一个全球的论坛系统:可以让中文和日文的用户都可以方便的浏览发表呢?数据应该以那种字符集存储呢?答案很简单:unicode。最后,我以GOOGLE的国际语言搜索引擎做一个设计实例说明如何实现国际化应用的设计:GOOGLE是一个非常好的国际化应用榜样(但我可没说GOOGLE是java做的哟)。
GOOGLE用户经常有这样的感觉:
为什么我第一次去GOOGLE,出现的就是中文的界面?
为什么在所有网站中查中文:有时候还会出日文网站的结果?比如:"google
秘密"
就以"google 秘密"这个查询为例:我们在输入框输入"google 秘密"
http://www.google.com/search?hl=zh-CN&newwindow=1&q=google+%C3%D8%C3%DC&btnG=Google%CB%D1%CB%F7&lr=
简单的流程说明如下:
输入:查询(按客户端编码方式)=>GOOLGE(将输入的字节流解码成unicode)=>查询unicode索引=>unicode结果集=>输出:查询结果(按客户端编码方式编码成字节流)
具体说明:
从以上的分析中我们可以看出:unicode实际非常漂亮的解决了应用的国际化问题
1
数据按可以转换成任意字符集的unicode方式集中存储(unicode inside)
2
然后根据客户端的本地化设置转换成本地字符集 (Locale outside)
如果在此之前的应用的汉化设计相当于UCDOS和RichWin的话,这种方式迟早要被内核汉化的WIN95淘汰的。毕竟核心级别对国际化的支持才是一个真正的简化应用设计的通用解决方案。Microsoft和Sun的很多产品从一开始就是为世界市场设计的,“汉化”思路的根源在于我们的软件开发中满足于自给自足的小农意识。
参考文档:
Java的国际化设计
http://java.sun.com/docs/books/tutorial/i18n/index.html
Linux 国际化本地化和中文化
http://www.linuxforum.net/doc/i18n-new.html
Linux 程序员必读:中文化与GB18030标准
http://www.ccidnet.com/tech/os/2001/07/31/58_2811.html
unicode FAQ
http://www.cl.cam.ac.uk/~mgk25/unicode.html
http://www.linuxforum.net/books/UTF-8-unicode.html
(中文版)
Java 编程技术中汉字问题的分析及解决
http://www-900.ibm.com/developerWorks/java/java_chinese/index.shtml
汉字的编码方式:
http://www.unihan.com.cn/cjk/ana17.htm
注释:
l10n i18n都是缩写:用的是英文单词的首位字母和其间字母个数
l10n: localization 本地化
i18n:
internationalization 国际化