|
在基于 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 ?cencoding gbk。 “ java 字节码->虚拟机->操作系统”, java 运行环境 (jre)分英文版和国际版,但只有国际版才支持非英文字符。 java 开发工具包 (jdk) 肯定支持多国字符,但并非所有的计算机用户都安装了 jdk 。很多操作系统及应用软件为了能够更好的支持 java ,都内嵌了 jre 的国际版本,为自己支持多国字符提供了方便。 “操作系统->显示设备”,对于汉字来说,操作系统必须支持并能够显示它。英文操作系统如果不搭配特殊的应用软件的话,是肯定不能够显示中文的。 还有一个问题,就是在 java 编程过程中,对中文字符进行正确的编码转换。例如,向网页输出中文字符串的时候,不论你是用 out.println(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 | ?span > |
.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 情况下程
|