Java编程技能中汉字问题的阐明及办理
副标题#e#
在基于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”);
#p#副标题#e#
#p#分页标题#e#
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 环境下措施仍然无法正常运行。
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 {
}
执行java PoorChinese后,我们会获得:
系统变量file.encoding的值为GBK ,user.language的值为zh ,user.region的值为CN ,这些系统变量的值抉择了系统默认的编码方法是GBK 。
在上述系统中,下面的代码将 GB2312 文件转换成 Big5 文件,它们可以或许辅佐我们领略 Java中汉字编码的转化:
#p#分页标题#e#
?
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;
}
}
}
?
编码转化的进程如下:
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 可以或许严格地测试它的产物,为用户带来更多的利便。
附一个用于从数据库和网络中取出中文乱码的处理惩罚函数,入参是有问题的字符串,出参是问题已包办理了的字符串。