forget and forget

能吃能睡是福;能忘是大福......

posts - 39, comments - 26, trackbacks - 0, articles - 10
  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理

Java中文问题的一般解决办法

Posted on 2006-03-29 11:17 橘子 阅读(788) 评论(0)  编辑  收藏 所属分类: WEB开发

汉字编码的常识

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


事实上,Java的中文问题都是由于Java应用所采用的缺省编码格式与目标或者应用所要读入字符的编码格式不同而造成的(具体参见文献1)。对于如何解决Java的中文问题,通常有四种方法:

1) 选择JDK的中文本地化版本。尽管Java2 JDK的中文本地化版本(http://java.sun.com/products/jdk/1.2/chinesejdk.html)并不是一个官方的版本,Sun公司也没有承诺会对该本地化版本进行升级,但其仍不失为一个Java中文问题的解决方案。

2) 选择合适的编译参数。对于Java的国际版本来讲,我们也可以在编译Java应用的时候通过指定确定的编码机制来实现其编译结果对中文的支持。例如,对于需要支持繁体中文和简体中文应用可以通过javac -encoding big5 sourcefile.java 和javac -encoding gb2312 sourcefile.java来编译源程序。

3) 通过编程的方式实现字符编码的转换代码。通过编程的方式来解决Java的中文问题,已经成为了一种较为普遍的做法。下面就是一种最常见的字符编码转换函数,其将字符的编码格式转换为中文Windows系统的GBK编码形式。

public   static  String toChinese(String strvalue)
   
{
     
try {
       
if (strvalue == null )
         
return   null ;
       
else
         
{
           strvalue 
=   new  String(strvalue.getBytes( " ISO8859_1 " ),  " GBK " );
           
return  strvalue;
         }

         }
catch (Exception e) {
               
return   null ;
      }

   }

4) 定义字符输出集。对于JSP应用,我们可以通过<%@ page contentType="text/html; charset=GBK" %>或<%@ page contentType="text/html; charset=GB2312" %>来定义JSP页面的字符输出集。当然,我们也可以通过HTML的标记<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=gb2312">来定义字符的输出集。

分析的原则

总的说来,所有解决Java中文处理的方法都不是很复杂。相反的是,由于Java技术特别是J2EE技术涉及的内容繁多,各种Web服务器、应用服务器以及JDBC数据库驱动等参差不齐,所以如何正确而及时的发现应用的中文处理问题则变得相对复杂的多。那么我们如何来发现这些问题呢?

通常,Java处理中文时所产生的问题都是由于用户的Java应用所采用的缺省编码格式与目标或者应用所要读入字符的编码格式不同而造成的,而引起这些不同的一个主要原因就是用户的Java应用与其它应用进行了编码格式不匹配的数据交换(包括直接或间接的数据输入、输出)。所以,为了及时发现问题,我们可以由这一点入手,根据以下的原则对应用进行分析:

  1. 注意字符变量情况。由于变量的字符编码形式较为隐蔽,多次变量间数值的改变和运算可能会引起字符集的改变;在变量与页面所提交数据的各种操作中,较容易发生不同编码格式字符进行运算的情况。
  2. 注意任何形式的字符读入与输出。之所以要提到任何形式,是因为Java应用大多数都是作为网络应用开发的,所以与其它语言的应用相比,Java应用需要面对网络世界各种各样的字符数据交换形式。例如各种表单的数据提交,URL形式的数据读入,经过加密运算的字符数据交换,网页控件选择结果的输入,控件内容的的显示(如List控件)等等。
  3. 小心使用第三方的组件和应用。由于第三方组件和应用的实现是非透明的,所以一般情况下,我们很难判断这些组件或驱动的缺省编码格式是什么,也无法对其进行控制。因此,在使用它们所提供的接口函数进行数据交换的时候要特别注意,如果确实出现中文无法正确处理情况,应首先检查我们自己的代码并调整相关代码以适应这些接口,因为这些组件或者应用基本上不会提供调整编码机制的接口。必要时,我们可能需要采用其它可替换的组件或者应用。
  4. 注意被请求对象所含有的数据输入与输出。这是非常隐蔽的一类情况,当我们的应用以对象的方式(例如序列化的对象)进行交互时,如果这个对象内部含有字符数据的处理过程,或者含有某些数据的输入、输出,甚至是抛出一段用中文注解的异常,都可能出现中文无法正确显示等问题。由于这些行为往往被封装在对象中,所以我们在编写程序时,很容易忽略这种可能情况。并且这种情况带有一定的不可预见性,例如我们可能不清楚这个对象会在什么时候抛出什么样的异常,所以这时我们就需要做一定的测试工作。
  5. 注意数据库的数据访问过程。Java通过JDBC与数据库建立连接。对于JDBC驱动程序来说,由于目前大部分的JDBC驱动程序并不是针对中文系统而设计的(中文数据大都采用ISO-8859-1编码方式),所以一般情况下在数据读写过程中往往都需要字符编码的转化。但是我们仍建议用户在使用这些JDBC驱动时,仔细阅读它的说明。如果确实无法弄清JDBC字符数据的编码到底是什么,我们的建议是做一些必要的测试。例如下面是一组在简体中文Win2000平台下,采用Weblogic 6.0所提供的JDBC驱动从MS SQL Server2000中正确读入中文字符的代码(例子中进行了字符运算):
        
    Class.forName(
    "weblogic.jdbc.mssqlserver4.Driver").newInstance();
          conn 
    = myDriver.connect("jdbc:weblogic:mssqlserver4", props);
          conn.setCatalog(
    "labmanager");
         Statement st 
    = conn.createStatement();
            
    //execute a query
        String  testStr;
    String testTempStr 
    = new String() ;
            testStr 
    = new String(testTempStr.getBytes("ISO-8859-1"));//编码转化
        DatabaseMetaData DBMetaData =conn.getMetaData();
        ResultSet rs 
    = DBMetaData.getTables(nullnull,null,new String[]{"TABLE"} );
        
    while (rs.next()){
            
    for(int j=1; j<=rs.getMetaData().getColumnCount(); j++){
    testStr 
    = testStr +String(rs.getObject(j).toString().getBytes("ISO-8859-1"));
                }

            }
  6. 然而,需要注意的是,不同的JDBC驱动对相同的数据库的支持并不同,而同一类JDBC驱动对不同的  数据库的支持也不相同,也就是说我们的字符转化代码在JDBC驱动改变甚至是版本变化情况下都有可能无法正确工作。例如对于上面的例子,在同样的环境下改用i-net 的Una 2000 Driver Version 2.03 for MS SQL Server时,是无法正确处理中文的。原因很简单,这个JDBC驱动本身支持的就是GBK的编码机制,所以根本就不需要做任何的编码转化。 
  7.  必要的测试。由于Java中文问题的产生随着Web服务器,浏览器,运行环境和开发工具的不同都可能发生变化,所以为了更好的避免问题的发生,我们必须作一些针对性的测试。另外,在我们确实无法通过分析来确定Java的中文处理问题是否可能发生的情况下或者无法知道问题的发生是由于哪个环节(是Web服务器,浏览器还是JDBC数据驱动等等)引起的时候,测试工作则变得非常重要。并且我们可能需要较为全面的测试,例如对Web服务器,浏览器和JDBC数据驱动等都要做测试,这样有利于我们找出那些隐藏在多个环节协调过程中所产生的问题。


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中文问题详解
http://www.cn-java.com/target/news.php?news_id=210
关于Java中文问题的几条分析原则http://www-900.ibm.com/developerWorks/cn/java/l-javachinese/index.shtml
全方位解决xml中文问题http://www.csdn.net/develop/read_article.asp?id=18901
servlet 中的汉字编码问题http://www-900.ibm.com/developerWorks/cn/java/jsp_dbcsz/index.shtml
Java程序的国际化和本地化介绍http://www-900.ibm.com/developerWorks/cn/java/joy-i18n/index.shtml
Java 编程技术中汉字问题的分析及解决http://www-900.ibm.com/developerWorks/cn/java/java_chinese/index.shtml
Unicode专题http://www-900.ibm.com/developerWorks/cn/theme/unicode.shtml
这里也有篇专家写的文章:
http://www.javaworld.com/javaworld/jw-04-2004/jw-0419-multibytes.html


只有注册用户登录后才能发表评论。


网站导航: