|
从汉化到国际化
——UniCode inside, Localization outsite
作者: 车东
hxxp://www.chedong.com/tech/unicode_java.html
内容摘要:
Java对输入输入首先有一个“字节流”到“字符流”之间的编码/解码过程,这个设置是根据系统配置决定的,为什么PHP之类的应用很少有字符集问题而Java有很好的国际化机制,却经常出现乱码问题呢?
简单的举例:
有一个包含“你好”这2个中文字的文件实际上是4个字节组成的:C4 E3 BA C3
在英文操作系统中缺省的编码解码方式是缺省编码方式是ISO8859,所以直接从文件中读取的结果是4个的字节,按ISO8859解码后在程序中操作的是4个Java字符,虽然每个JAVA字符是16位Unicode,但每个字符仍是8位字节的映射\u00C4\u00E3\u00BA\u00C3,因此处理的仍是“英文”。而显示过程中,是浏览器将字节流正确的显示成了相应的中文。
而一个Java应用在GBK编码方式的操作系统中,直接从文件中读取4个的字节后,按GBK方式解码后是2个16位的Java字符\u4F60\u597D,每个字都是相应Unicode的CJK区块所对应的中文。
更多的例子请参考:Java的中文处理学习笔记
这也就是为什么在php等应用很少出字符集问题的原因:在服务器端环境缺省一般是英文(ISO8859-1),等于全部处理使用的都是按字节方式处理的。数据输入输出过程中编码方式完全不被改动,因此乱码问题很少出现。而Java实际上提供了把每个中文直接当成1个“字”而不是2个字节处理的机制,主要的乱码问题往往是输入输出时编码解码方式不一致造成的。而且通过Unicode机制,程序除了实现程序界面根据本地化的适应外,甚至程序处理的内容本身的在不同字符集的操作系统中也是可以通用的,比如:在繁体中文操作系统中编辑的内容,在简体中文操作系统中也能正常的查询。以下例子可能更说明问题:
JAVA应用的中文问题:
1) 如何通过GNU/Linux系统的本地化设置让JAVA应用支持中文
2) Java Web应用设计中的中文问题:通过web.xml设置解决URLEncoder.encode() 方法和系统缺省编码方式相关的问题
3) 以GOOGLE的搜索引擎为例:说明如何将国际化和本地化应用到自己的应用设计中(UniCode inside Localization outsite)
通过GNU/Linux系统的本地化设置让JAVA应用支持中文
Java 编程技术中汉字问题的分析及解决这篇直到最近还经常被一些网站转贴的文章中有一个例子说明了很多中国程序员遇到汉字乱码问题的思路:"GB2312 it"(汉化)
原文如下:
=====
......前不久,我的一位技术上的朋友发信给我说,他终于找到了 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:
首先,在Servlet层就不应该考虑中文输出的问题,因为,在MVC的设计模式中,Servlet主要的角色是Contrallor,所以,在这一层中,数据应该最好还是Unicode形式,在最后让Jsp或者通过xslt做针对客户端浏览器的输出时,再需要考虑本地字符集编码的问题。
作为一个标准的国际化应用,JAVA应用的缺省编码方式不应该是在WEB应用这一层设置的,而是JVM根据系统缺省编码方式根据操作系统的环境设置(locale, 包括字符集,日期格式等本地化环境)改变来实现。
如何设置可以让GNU/Linux从系统层次就支持中文编码(让JVM缺省的file.encoding就按照中文GB2312或者GBK进行编码解码)呢?
以上这篇文章发表在2000年年底,当时的GNU/Linux的内核是基于glibc-2.1开发的,而GLIBC2.1对中文的locale支持还有限,因此,在GNU/Linux上不能根据locale的设置将系统缺省的编码方式变成GB2312,从而改变JVM缺省的编码方式。关于GNU/Linux对l10n的支持请看:GNU/Linux程序员必读:中文化与GB18030标准。所以在redhat6.x下,无论你怎么设置locale,系统缺省的缺省file.encoding都是ISO_8859_1。
在redhat7.x 系统内核所基于的glibc-2.2.x对l10n有了更完整的支持,所以可以通过设置
LC_ALL=zh_CN.GB2312;export LC_ALL
LANG=zh_CN.GB2312;export LANG
让系统缺省的编码方式变成GB2312。JVM会根据系统的缺省编码方式设置系统的file.encoding属性。之后,应用中任何字节流到字符流的转换,JVM都会按照这一系统缺省编码方式进行转换。因此在基于glibc-2.2以上的GNU/Linux上是可以通过locale的设置来改变系统缺省的编码方式,从而改变上层Java应用的缺省编码、解码方式的。
locale设置对JVM file.encoding的影响可以我通过做过的一个试验说明:hello_unicode.html
由此可见:
1) GNU/Linux是依靠GNU的工具发展起来的:没有GNU就没有GNU/Linux。所以GNU/Linux对本地化的支持,也是在核心的glibc-2.2.x对中文locale有了更好的支持以后才逐步发展起来的。
2) GNU/Linux对国际化的支持远远落后于WINDOWS SOLARIS等商业操作系统:2年甚至更多。
通过web.xml设置解决URLEncoder.encode()方法和系统缺省编码方式相关的问题
在我所理解的范围内,J2SE 1.3中非常不符合Java的国际化规范的是在使用URLEncoder的时候:
比如在中文WIN98上运行的应用,使用URLEncoder.encode(String s)时:比如“中文”这2个字符直接被Encoding的话结果是"%3F%3F"=>"??"。原因很简单,“中文”在encode()过程中应该先按GBK编码方式编码成4个字节(\u00d6\u00d0\u00ce\u00c4)后再进行URLEncoding才是正确的。这个在J2SE1.4中也修正了。方法encode(String s)已经不鼓励使用,取而代之的是除了需要进行URLEncoding的字符串外,同时需要指定字符串编码方式的encode(String s, String enc)。这样,URLEncoder就可以和系统缺省的编码方式无关了。
在J2SE1.3下一个WEB应用如果有这个问题可以通过在WEB-INF/web.xml中设置character-encoding来解决:
<web-app character-encoding="your_system_default_file.encoding">
...
</web-app>
比如:产品是在中文WINDOWS98中运行,系统缺省字符集是用GBK,则这个应用的web.xml需要设置成:
<web-app character-encoding="GBK">
...
</web-app> |
|