Java编程中汉字问题的分析及解决

原创 2007年10月11日 20:23:00
引:在基于Java语言的编程中,我们经常碰到汉字的处理及显示的问题。一大堆看不懂的乱码肯定不是我们愿意看到的显示效果,怎样才能够让那些汉字正确显示呢?Java语言默认的编码方式是UNICODE,而我们中国人通常使用的文件和数据库都

  在基于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  

  .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中汉字编码的转化:

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能够严格地测试它的产品,为用户带来更多的方便。

  附一个用于从数据库和网络中取出中文乱码的处理函数,入参是有问题的字符串,出参是问题已经解决了的字符串。

 

JAVA中文乱码问题的产生和建议解决办法

开发java应用出现乱码是很常见的,毕竟现在unicode的使用还不是很广泛,在使用gb2312(包含了gbk简体,big5繁体)的系统中要正确  实现中文的display和数据库的存储是最基本的要...
  • LVGAOYANH
  • LVGAOYANH
  • 2015年08月02日 12:08
  • 4269

java中文乱码解决之道(五)-----java是如何编码解码的

在上篇博客中LZ阐述了java各个渠道转码的过程,阐述了java在运行过程中那些步骤在进行转码,在这些转码过程中如果一处出现问题就很有可能会产生乱码!下面LZ就讲述java在转码过程中是如何来进行编码...
  • chenssy
  • chenssy
  • 2015年01月20日 09:29
  • 66607

读者与写者问题

在进行多线程编程的过程中,线程间的同步与互斥是件需要认真考虑的关键点,而读者与写者就是线程间同步的典型例子:若干个读者在读取文章,若干个写者同时编辑文章,保证多个读者和多个写者能并发或并行(关于并行与...
  • xj2419174554
  • xj2419174554
  • 2014年11月06日 21:12
  • 743

Java编程技术中汉字问题的分析及解决

在基于 Java 语言的编程中,我们经常碰到汉字的处理及显示的问题。一大堆看不懂的乱码肯定不是我们愿意看到的显示效果,怎样才能够让那些汉字正确显示呢?Java语言默认的编码方式是UNICODE,而我们...
  • zhang_zhiwei2
  • zhang_zhiwei2
  • 2013年09月06日 13:28
  • 294

Java 编程技术中汉字问题的分析及解决

在基于 Java 语言的编程中,我们经常碰到汉字的处理及显示的问题。一大堆看不懂的乱码肯定不是我们愿意看到的显示效果,怎样才能够让那些汉字正确显示呢?Java 语言默认的编码方式是UNICODE ,而...
  • lshihao1
  • lshihao1
  • 2014年09月05日 08:55
  • 590

Java编程技术中汉字问题的分析及解决

在基于 Java 语言的编程中,我们经常碰到汉字的处理及显示的问题。一大堆看不懂的乱码肯定不是我们愿意看到的显示效果,怎样才能够让那些汉字正确显示呢?Java语言默认的编码方式是UNICODE,而我们...
  • u011122762
  • u011122762
  • 2013年09月05日 15:03
  • 287

web开发中文乱码问题及解决方案

在web应用开发过程中,经常会碰到中文乱码的问题,下面是常见的导致中文乱码的问题以及解决方案(tomcat) 1.JSP页面中包含中文不能保存,eclipse中提示需保存为UTF-8 解决的办法是在j...
  • sunmun
  • sunmun
  • 2016年07月14日 13:09
  • 1245

每个程序员1小时内必须解决的5个编程问题--解答代码

每个程序员1小时内必须解决的5个编程问题,我的解答代码
  • u013073227
  • u013073227
  • 2015年05月16日 18:09
  • 696

【算法编程】小学数学题难倒博士

昨天在科学网上得知这样一个新闻《越南小学数学题难倒博士》,据悉题目来自越南保禄小学三年班,不过报道称该题难倒了上至博士下至家长,未免也太言过其实了。 题目描述 学生需要在下图表格中按由上至下、...
  • tengweitw
  • tengweitw
  • 2015年05月27日 09:48
  • 2913

约瑟夫问题(Java实现)

一、简介约瑟夫问题(有时也称为约瑟夫斯置换,是一个出现在计算机科学和数学中的问题。在计算机编程的算法中,类似问题又称为约瑟夫环。又称“丢手绢问题”.)例子: len个人围成一个圈,玩丢手绢...
  • u013255737
  • u013255737
  • 2016年08月24日 21:32
  • 3189
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Java编程中汉字问题的分析及解决
举报原因:
原因补充:

(最多只允许输入30个字)