论坛 >> 编程探讨                     阅读数: 1735
JSP/Servlet 中的汉字编码问题(转)

作者:UB    时间:2002-06-14 10:51:10 

JSP/Servlet中的汉字编码问题(1)

[作者:不详添加时间:2001-9-68:12:47>




网上就JSP/Servlet中DBCS字符编码问题有许多优秀的文章和讨论,本文对它们作一些整理,并结合IBMWebSphereApplicationServer3.5(WAS)的解决方法作一些说明,希望它不是多余的。

内容:
问题的起源
GB2312-80,GBK,GB18030-2000汉字字符集及Encoding
中文转码时'?'、乱码的由来
JSP/Servlet汉字编码问题及在WAS中的解决办法
结束语


1.问题的起源

每个国家(或区域)都规定了计算机信息交换用的字符编码集,如美国的扩展ASCII码,中国的GB2312-80,日本的JIS等,作为该国家/区域内信息处理的基础,有着统一编码的重要作用。字符编码集按长度分为SBCS(单字节字符集),DBCS(双字节字符集)两大类。早期的软件(尤其是操作系统),为了解决本地字符信息的计算机处理,出现了各种本地化版本(L10N),为了区分,引进了LANG,Codepage等概念。但是由于各个本地字符集代码范围重叠,相互间信息交换困难;软件各个本地化版本独立维护成本较高。因此有必要将本地化工作中的共性抽取出来,作一致处理,将特别的本地化处理内容降低到最少。这也就是所谓的国际化(I18N)。各种语言信息被进一步规范为Locale信息。处理的底层字符集变成了几乎包含了所有字形的Unicode。

现在大部分具有国际化特征的软件核心字符处理都是以Unicode为基础的,在软件运行时根据当时的Locale/Lang/Codepage设置确定相应的本地字符编码设置,并依此处理本地字符。在处理过程中需要实现Unicode和本地字符集的相互转换,甚或以Unicode为中间的两个不同本地字符集的相互转换。这种方式在网络环境下被进一步延伸,任何网络两端的字符信息也需要根据字符集的设置转换成可接受的内容。

Java语言内部是用Unicode表示字符的,遵守UnicodeV2.0。Java程序无论是从/往文件系统以字符流读/写文件,还是往URL连接写HTML信息,或从URL连接读取参数值,都会有字符编码的转换。这样做虽然增加了编程的复杂度,容易引起混淆,但却是符合国际化的思想的。

从理论上来说,这些根据字符集设置而进行的字符转换不应该产生太多问题。而事实是由于应用程序的实际运行环境不同,Unicode和各个本地字符集的补充、完善,以及系统或应用程序实现的不规范,转码时出现的问题时时困扰着程序员和用户。

2.GB2312-80,GBK,GB18030-2000汉字字符集及Encoding

其实解决JAVA程序中的汉字编码问题的方法往往很简单,但理解其背后的原因,定位问题,还需要了解现有的汉字编码和编码转换。

GB2312-80是在国内计算机汉字信息技术发展初始阶段制定的,其中包含了大部分常用的一、二级汉字,和9区的符号。该字符集是几乎所有的中文系统和国际化的软件都支持的中文字符集,这也是最基本的中文字符集。其编码范围是高位0xa1-0xfe,低位也是0xa1-0xfe;汉字从0xb0a1开始,结束于0xf7fe;

GBK是GB2312-80的扩展,是向上兼容的。它包含了20902个汉字,其编码范围是0x8140-0xfefe,剔除高位0x80的字位。其所有字符都可以一对一映射到Unicode2.0,也就是说JAVA实际上提供了GBK字符集的支持。这是现阶段Windows和其它一些中文操作系统的缺省字符集,但并不是所有的国际化软件都支持该字符集,感觉是他们并不完全知道GBK是怎么回事。值得注意的是它不是国家标准,而只是规范。随着GB18030-2000国标的发布,它将在不久的将来完成它的历史使命。

GB18030-2000(GBK2K)在GBK的基础上进一步扩展了汉字,增加了藏、蒙等少数民族的字形。GBK2K从根本上解决了字位不够,字形不足的问题。它有几个特点,

它并没有确定所有的字形,只是规定了编码范围,留待以后扩充。
编码是变长的,其二字节部分与GBK兼容;四字节部分是扩充的字形、字位,其编码范围是首字节0x81-0xfe、二字节0x30-0x39、三字节0x81-0xfe、四字节0x30-0x39。
它的推广是分阶段的,首先要求实现的是能够完全映射到Unicode3.0标准的所有字形。
它是国家标准,是强制性的。
现在还没有任何一个操作系统或软件实现了GBK2K的支持,这是现阶段和将来汉化的工作内容。
Unicode的介绍......就免了吧。

JAVA支持的encoding中与中文编程相关的有:(有几个在JDK文档中未列出)ASCII7-bit,同ascii7
ISO8859-18-bit,同8859_1,ISO-8859-1,ISO_8859-1,latin1...
GB2312-80同gb2312,gb2312-1980,EUC_CN,euccn,1381,Cp1381,1383,Cp1383,ISO2022CN,ISO2022CN_GB......
GBK(注意大小写),同MS936
UTF8UTF-8
GB18030(现在只有IBMJDK1.3.?有支持),同Cp1392,1392


JAVA语言采用Unicode处理字符.但从另一个角度来说,在java程序中也可以采用非Unicode的转码,重要的是保证程序入口和出口的汉字信息不失真。如完全采用ISO-8859-1来处理汉字也能达到正确的结果。网络上流行的许多解决方法,都属于这种类型。为了不致引起混淆,本文不对这种方法作讨论。

3.中文转码时'?'、乱码的由来

两个方向转换都有可能得到错误的结果:

Unicode-->Byte,如果目标代码集不存在对应的代码,则得到的结果是0x3f.
如:
"\u00d6\u00ec\u00e9\u0046\u00bb\u00f9".getBytes("GBK")的结果是"?ìéF?ù",Hex值是3fa8aca8a6463fa8b4.
仔细看一下上面的结果,你会发现\u00ec被转换为0xa8ac,\u00e9被转换为\xa8a6...它的实际有效位变长了!这是因为GB2312符号区中的一些符号被映射到一些公共的符号编码,由于这些符号出现在ISO-8859-1或其它一些SBCS字符集中,故它们在Unicode中编码比较靠前,有一些其有效位只有8位,和汉字的编码重叠(其实这种映射只是编码的映射,在显示时仔细不是一样的。Unicode中的符号是单字节宽,汉字中的符号是双字节宽).在Unicode\u00a0--\u00ff之间这样的符号有20个。了解这个特征非常重要!由此就不难理解为什么JAVA编程中,汉字编码的错误结果中常常会出现一些乱码(其实是符号字符),而不全是'?'字符,就比如上面的例子。

Byte-->Unicode,如果Byte标识的字符在源代码集不存在,则得到的结果是0xfffd.
如:
Byteba[>={(byte)0x81,(byte)0x40,(byte)0xb0,(byte)0xa1};newString(ba,"gb2312");
结果是"?啊",hex值是"\ufffd\u554a".0x8140是GBK字符,按GB2312转换表没有对应的值,取\ufffd.(请注意:在显示该uniCode时,因为没有对应的本地字符,所以也适用上一种情况,显示为一个"?".)

实际编程中,JSP/Servlet程序得到错误的汉字信息,往往是这两个过程的叠加,有时甚至是两个过程叠加后反复作用的结果.

4.JSP/Servlet汉字编码问题及在WAS中的解决办法

4.1常见的encoding问题的现象
网上常出现的JSP/Servletencoding问题一般都表现在browser或应用程序端,如:
浏览器中看到的Jsp/Servlet页面中的汉字怎么都成了’?’?
浏览器中看到的Servlet页面中的汉字怎么都成了乱码?
JAVA应用程序界面中的汉字怎么都成了方块?
Jsp/Servlet页面无法显示GBK汉字。
JSP页面中内嵌在<%...%>,<%=...%>等Tag包含的JAVAcode中的中文成了乱码,但页面的其它汉字是对的。
Jsp/Servlet不能接收form提交的汉字。
JSP/Servlet数据库读写无法获得正确的内容。
隐藏在这些问题后面的是各种错误的字符转换和处理(除第3个外,是因为Javafont设置错误引起的)。解决类似的字符encoding问题,需要了解Jsp/Servlet的运行过程,检查可能出现问题的各个点。

4.2JSP/Servletweb编程时的encoding问题
运行于Java应用服务器的JSP/Servlet为Browser提供HTML内容,其过程如下图所示:



其中有字符编码转换的地方有:

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字符乱码问题


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参数就可以了。


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应当能够确保在这个阶段将嵌入的汉字正确传送出去。


这是解释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,这将有助于彻底解决这个问题。
4.3IBMWebsphereApplicationServer中的解决方法

WebSphereApplicationServer对标准的ServletAPI2.x作了扩展,提供较好的多语言支持。运行在中文的操作系统中,可以不作任何设置就可以很好地处理汉字。下面的说明只是对WAS是运行在英文的系统中,或者需要有GBK支持时有效。

上述c,d情况,WAS都要查询Browser的语言设置,在缺省状况下,zh,zh-cn等均被映射为JAVAencodingCP1381(注意:CP1381只是等同于GB2312的一个codepage,没有GBK支持)。这样做我想是因为无法确认Browser运行的操作系统是支持GB2312,还是GBK,所以取其小。但是实际的应用系统还是要求页面中出现GBK汉字,最著名的是朱总理名字中的“镕"(rong2,0xe946,\u9555),所以有时还是需要将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。

上面列出的问题中还有一个关于Tag<%...%>,<%=...%>中的JAVA代码里包含的静态文本未能正确显示的问题,在WAS中的解决方法是除了设置正确的file.encoding,还需要以相同方法设置-Duser.language=zh-Duser.region=CN。这与JAVAlocale的设置有关。

4.4数据库读写时的encoding问题

JSP/Servlet编程中经常出现encoding问题的另一个地方是读写数据库中的数据。

流行的关系数据库系统都支持数据库encoding,也就是说在创建数据库时可以指定它自己的字符集设置,数据库的数据以指定的编码形式存储。当应用程序访问数据时,在入口和出口处都会有encoding转换。对于中文数据,数据库字符编码的设置应当保证数据的完整性.GB2312,GBK,UTF-8等都是可选的数据库encoding;也可以选择ISO8859-1(8-bit),那么应用程序在写数据之前须将16Bit的一个汉字或Unicode拆分成两个8-bit的字符,读数据之后则需将两个字节合并起来,同时还要判别其中的SBCS字符。没有充分利用数据库encoding的作用,反而增加了编程的复杂度,ISO8859-1不是推荐的数据库encoding。JSP/Servlet编程时,可以先用数据库管理系统提供的管理功能检查其中的中文数据是否正确。

然后应当注意的是读出来的数据的encoding,JAVA程序中一般得到的是Unicode。写数据时则相反。

4.5定位问题时常用的技巧

定位中文encoding问题通常采用最笨的也是最有效的办法——在你认为有嫌疑的程序处理后打印字符串的内码。通过打印字符串的内码,你可以发现什么时候中文字符被转换成Unicode,什么时候Unicode被转回中文内码,什么时候一个中文字成了两个Unicode字符,什么时候中文字符串被转成了一串问号,什么时候中文字符串的高位被截掉了……

取用合适的样本字符串也有助于区分问题的类型。如:”aa啊aa丂aa”等中英相间、GB、GBK特征字符均有的字符串。一般来说,英文字符无论怎么转换或处理,都不会失真(如果遇到了,可以尝试着增加连续的英文字母长度)。

5.结束语

其实JSP/Servlet的中文encoding并没有想像的那么复杂,虽然定位和解决问题没有定规,各种运行环境也各不尽然,但后面的原理是一样的。了解字符集的知识是解决字符问题的基础。不过,随着中文字符集的变化,不仅仅是java编程,中文信息处理中的问题还是会存在一段时间的。


...........................UB修改于2002-06-14 10:52:07



RE:JAVA编程技术中汉字问题的分析及解决(转)

作者:UB    时间:2002-07-22 17:04:00    [修改]    [回复]    [删除]

JAVA编程技术中汉字问题的分析及解决
文章来源:www.ibm.com
在基于Java语言的编程中,我们经常碰到汉字的处理及显示的问题。一大堆看不懂的乱码肯定不是我们愿意看到的显示效果,怎样才能够让那些汉字正确显示呢?Java语言默认的编码方式是UNICODE,而我们中国人通常使用的文件和数据库都是基于GB2312或者BIG5等方式编码的,怎样才能够恰当地选择汉字编码方式并正确地处理汉字的编码呢?本文将从汉字编码的常识入手,结合Java编程实例,分析以上两个问题并提出解决它们的方案。

现在Java编程语言已经广泛应用于互联网世界,早在Sun公司开发Java语言的时候,就已经考虑到对非英文字符的支持了。Sun公司公布的Java运行环境(JRE)本身就分英文版和国际版,但只有国际版才支持非英文字符。不过在Java编程语言的应用中,对中文字符的支持并非如同JavaSoft的标准规范中所宣称的那样完美,因为中文字符集不只一个,而且不同的操作系统对中文字符的支持也不尽相同,所以会有许多和汉字编码处理有关的问题在我们进行应用开发中困扰着我们。有很多关于这些问题的解答,但都比较琐碎,并不能够满足大家迫切解决问题的愿望,关于Java中文问题的系统研究并不多,本文从汉字编码常识出发,分析Java中文问题,希望对大家解决这个问题有所帮助。

汉字编码的常识
我们知道,英文字符一般是以一个字节来表示的,最常用的编码方法是ASCII。但一个字节最多只能区分256个字符,而汉字成千上万,所以现在都以双字节来表示汉字,为了能够与英文字符分开,每个字节的最高位一定为1,这样双字节最多可以表示64K格字符。我们经常碰到的编码方式有GB2312、BIG5、UNICODE等。关于具体编码方式的详细资料,有兴趣的读者可以查阅相关资料。我肤浅谈一下和我们关系密切的GB2312和UNICODE。GB2312码,中华人民共和国国家标准汉字信息交换用编码,是一个由中华人民共和国国家标准总局发布的关于简化汉字的编码,通行于中国大陆地区及新加坡,简称国标码。两个字节中,第一个字节(高字节)的值为区号值加32(20H),第二个字节(低字节)的值为位号值加32(20H),用这两个值来表示一个汉字的编码。UNICODE码是微软提出的解决多国字符问题的多字节等长编码,它对英文字符采取前面加“0”字节的策略实现等长兼容。如“A”的ASCII码为0x41,UNICODE就为0x00,0x41。利用特殊的工具各种编码之间可以互相转换。

Java中文问题的初步认识
我们基于Java编程语言进行应用开发时,不可避免地要处理中文。Java编程语言默认的编码方式是UNICODE,而我们通常使用的数据库及文件都是基于GB2312编码的,我们经常碰到这样的情况:浏览基于JSP技术的网站看到的是乱码,文件打开后看到的也是乱码,被Java修改过的数据库的内容在别的场合应用时无法继续正确地提供信息。

StringsEnglish=“apple”;
StringsChinese=“苹果”;
Strings=“苹果apple”;

sEnglish的长度是5,sChinese的长度是4,而s默认的长度是14。对于sEnglish来说,Java中的各个类都支持得非常好,肯定能够正确显示。但对于sChinese和s来说,虽然JavaSoft声明Java的基本类已经考虑到对多国字符的支持(默认UNICODE编码),但是如果操作系统的默认编码不是UNICODE,而是国标码等。从Java源代码到得到正确的结果,要经过“Java源代码->Java字节码->;虚拟机->操作系统->显示设备”的过程。在上述过程中的每一步骤,我们都必须正确地处理汉字的编码,才能够使最终的显示结果正确。

“Java源代码->Java字节码”,标准的Java编译器javac使用的字符集是系统默认的字符集,比如在中文Windows操作系统上就是GBK,而在Linux操作系统上就是ISO-8859-1,所以大家会发现在Linux操作系统上编译的类中源文件中的中文字符都出了问题,解决的办法就是在编译的时候添加encoding参数,这样才能够与平台无关。用法是

javac–encodingGBK。

“Java字节码->虚拟机->操作系统”,Java运行环境(JRE)分英文版和国际版,但只有国际版才支持非英文字符。Java开发工具包(JDK)肯定支持多国字符,但并非所有的计算机用户都安装了JDK。很多操作系统及应用软件为了能够更好的支持Java,都内嵌了JRE的国际版本,为自己支持多国字符提供了方便。

“操作系统->显示设备”,对于汉字来说,操作系统必须支持并能够显示它。英文操作系统如果不搭配特殊的应用软件的话,是肯定不能够显示中文的。

还有一个问题,就是在Java编程过程中,对中文字符进行正确的编码转换。例如,向网页输出中文字符串的时候,不论你是用

out.println(string);//string是含中文的字符串

还是用

<%=string%>,都必须作UNICODE到GBK的转换,或者手动,或者自动。在JSP1.0中,可以定义输出字符集,从而实现内码的自动转换。用法是

<%@pageContentType=”text/html;charset=gb2312”%>

但是在一些JSP版本中并没有提供对输出字符集的支持,(例如JSP0.92),这就需要手动编码输出了,方法非常多。最常用的方法是

Strings1=request.getParameter(“keyword”);
Strings2=newString(s1.getBytes(“ISO-8859-1”),”GBK”);

getBytes方法用于将中文字符以“ISO-8859-1”编码方式转化成字节数组,而“GBK”是目标编码方式。我们从以ISO-8859-1方式编码的数据库中读出中文字符串s1,经过上述转换过程,在支持GBK字符集的操作系统和应用软件中就能够正确显示中文字符串s2。

Java中文问题的表层分析及处理

背景
开发环境JDK1.15Vcafe2.0JPadPro
服务器端NTIISSybaseSystemJconnect(JDBC)
客户端IE5.0Pwin98

CLASS文件存放在服务器端,由客户端的浏览器运行APPLET,APPLET只起调入FRAME类等主程序的作用。界面包括Textfield,TextArea,List,Choice等。

I.取中文
用JDBC执行SELECT语句从服务器端读取数据(中文)后,将数据用APPEND方法加到TextArea(TA),不能正确显示。但加到List中时,大部分汉字却可正确显示。
将数据按“ISO-8859-1”编码方式转化为字节数组,再按系统缺省编码方式(DefaultCharacterEncoding)转化为STRING,即可在TA和List中正确显示。
程序段如下:

dbstr2=results.getString(1);
//AfterreadingtheresultfromDBserver,convertingittostring.
dbbyte1=dbstr2.getBytes(“iso-8859-1”);
dbstr1=newString(dbbyte1);

在转换字符串时不采用系统默认编码方式,而直接采用“GBK”或者“GB2312”,在A和B两种情况下,从数据库取数据都没有问题。

II.写中文到数据库
处理方式与“取中文”相逆,先将SQL语句按系统缺省编码方式转化为字节数组,再按“ISO-8859-1”编码方式转化为STRING,最后送去执行,则中文信息可正确写入数据库。

程序段如下:
sqlstmt=tf_input.getText();
//BeforesendingstatementtoDBserver,convertingittosqlstatement.
dbbyte1=sqlstmt.getBytes();
sqlstmt=newString(dbbyte1,”iso-8859-1”);
_stmt=_con.createStatement();
_stmt.executeUpdate(sqlstmt);
……

问题:如果客户机上存在CLASSPATH指向JDK的CLASSES.ZIP时(称为A情况),上述程序代码可正确执行。但是如果客户机只有浏览器,而没有JDK和CLASSPATH时(称为B情况),则汉字无法正确转换。

我们的分析:
1.经过测试,在A情况下,程序运行时系统的缺省编码方式为GBK或者GB2312。在B情况下,程序启动时浏览器的JAVA控制台中出现如下错误信息:
Can'tfindresourceforsun.awt.windows.awtLocalization_zh_CN
然后系统的缺省编码方式为“8859-1”。

2.如果在转换字符串时不采用系统缺省编码方式,而是直接采用“GBK”或“GB2312”,则在A情况下程序仍然可正常运行,在B情况下,系统出现错误:
UnsupportedEncodingException。

3.在客户机上,把JDK的CLASSES.ZIP解压后,放在另一个目录中,CLASSPATH只包含该目录。然后一边逐步删除该目录中的.CLASS文件,另一边运行测试程序,最后发现在一千多个CLASS文件中,只有一个是必不可少的,该文件是:sun.io.CharToByteDoubleByte.class。

将该文件拷到服务器端和其它的类放在一起,并在程序的开头IMPORT它,在B情况下程序仍然无法正常运行。

4.在A情况下,如果在CLASSPTH中去掉sun.io.CharToByteDoubleByte.class,则程序运行时测得默认编码方式为“8859-1”,否则为“GBK”或“GB2312”。
如果JDK的版本为1.2以上的话,在B情况下遇到的问题得到了很好的解决,测试的步骤同上,有兴趣的读者可以尝试一下。

Java中文问题的根源分析及解决
在简体中文MSWindows98+JDK1.3下,可以用System.getProperties()得到Java运行环境的一些基本属性,类PoorChinese可以帮助我们得到这些属性。
类PoorChinese的源代码:

publicclassPoorChinese{
publicstaticvoidmain(String[>args){
System.getProperties().list(System.out);
}
}

执行javaPoorChinese后,我们会得到:

系统变量file.encoding的值为GBK,user.language的值为zh,user.region的值为CN,这些系统变量的值决定了系统默认的编码方式是GBK。

在上述系统中,下面的代码将GB2312文件转换成Big5文件,它们能够帮助我们理解Java中汉字编码的转化:

importjava.io.*;
importjava.util.*;

publicclassgb2big5{

staticintiCharNum=0;

publicstaticvoidmain(String[>args){
System.out.println("InputGB2312file,outputBig5file.");
if(args.length!=2){
System.err.println("Usage:jviewgb2big5gbfilebig5file");
System.exit(1);
}
StringinputString=readInput(args[0>);
writeOutput(inputString,args[1>);
System.out.println("NumberofCharactersinfile:"+iCharNum+".");
}

staticvoidwriteOutput(Stringstr,StringstrOutFile){
try{
FileOutputStreamfos=newFileOutputStream(strOutFile);
Writerout=newOutputStreamWriter(fos,"Big5");
out.write(str);
out.close();
}
catch(IOExceptione){
e.printStackTrace();
e.printStackTrace();
}
}

staticStringreadInput(StringstrInFile){
StringBufferbuffer=newStringBuffer();
try{
FileInputStreamfis=newFileInputStream(strInFile);
InputStreamReaderisr=newInputStreamReader(fis,"GB2312");
Readerin=newBufferedReader(isr);
intch;
while((ch=in.read())>-1){
iCharNum+=1;
buffer.append((char)ch);
}
in.close();
returnbuffer.toString();
}
catch(IOExceptione){
e.printStackTrace();
returnnull;
}
}
}

编码转化的过程如下:

ByteToCharGB2312CharToByteBig5
GB2312------------------>Unicode------------->Big5

执行javagb2big5gb.txtbig5.txt,如果gb.txt的内容是“今天星期三”,则得到的文件big5.txt中的字符能够正确显示;而如果gb.txt的内容是“情人节快乐”,则得到的文件big5.txt中对应于“节”和“乐”的字符都是符号“?”(0x3F),可见sun.io.ByteToCharGB2312和sun.io.CharToByteBig5这两个基本类并没有编好。

正如上例一样,Java的基本类也可能存在问题。由于国际化的工作并不是在国内完成的,所以在这些基本类发布之前,没有经过严格的测试,所以对中文字符的支持并不像JavaSoft所声称的那样完美。前不久,我的一位技术上的朋友发信给我说,他终于找到了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里面。

五、关于Java中文问题的总结
Java编程语言成长于网络世界,这就要求Java对多国字符有很好的支持。Java编程语言适应了计算的网络化的需求,为它能够在网络世界迅速成长奠定了坚实的基础。Java的缔造者(JavaSoft)已经考虑到Java编程语言对多国字符的支持,只是现在的解决方案有很多缺陷在里面,需要我们付诸一些补偿性的措施。而世界标准化组织也在努力把人类所有的文字统一在一种编码之中,其中一种方案是ISO10646,它用四个字节来表示一个字符。当然,在这种方案未被采用之前,还是希望JavaSoft能够严格地测试它的产品,为用户带来更多的方便。

附一个用于从数据库和网络中取出中文乱码的处理函数,入参是有问题的字符串,出参是问题已经解决了的字符串。
StringparseChinese(Stringin)
{
Strings=null;
bytetemp[>;
if(in==null)
{
System.out.println("Warn:Chinesenullfounded!");
returnnewString("");
}
try
{
temp=in.getBytes("iso-8859-1");
s=newString(temp);
}
catch(UnsupportedEncodingExceptione)
{
System.out.println(e.toString());
}
returns;
}