Java资源网

| JAVA基础 | 环境配置 | JDBC | 线程技术 | Socket编程 | JavaMail | JAVA与XML | 设计模式 | 技术新闻 | Java认证 | 程序人生 软件下载
| JSP&Servlet | Spring | Struts | Hibernate | JBuilder | Eclipse | WebService | EJB技术 | J2ME开发 | 应用服务器 | JXTA | Ajax
Articles search文章搜索
   关键字:
   类 别:
       
New download 最新下载
· [组件]HTML Parser 1.5
· [教程]WebSphere Studio应用教程
· [组件]JDom 1.0
· [工具]Junit3.8.1
· [教程]EJB编程及J2EE系统架构和设计
· [教程]EJB教程
· [教程]J2EE Tutorial中文版
· [教程]Java编程思想2(英文)
· [教程]java编程思想(完整版)
· [教程]Java网络编程
New articles 最新文章
· 设计移动 Web 服务
· 解析XML的时候完全忽略DTD
· 理解XML Schema XML Schema 初步
· 标签库的深入研究
· 提升JSP应用程序的七大绝招
· 如何使用JDOM对XML文件进行操作
· 处理XML字符串中特殊字符
· 利用Digester把XML转换成为Java对象
· 使用WebService 和RMI远程协作
· 使用Axis开发Web Service程序
Articles top 热门文章
· Eclipse基础--plugin插件安装(6644)
· eclipse+tomcat+lomboz的安装配置说明(4774)
· Java程序员就业前景(4584)
· Windows下JAVA环境变量的设置祥解(3788)
· Tomcat下JSP、Servlet和JavaBean环境的配置(3716)
· 使用links方式安装Eclipse插件(3698)
· 一个老程序员的心理话(3533)
· linux下jdk的安装与配置(3459)
· 初学者入门:Structs中基本配置入门(3334)
· Eclipse 运行命令行参数大全(3084)
您的位置:首页>>JAVA基础>>JAVA中文比较问题的分析解决
JAVA中文比较问题的分析解决
2005-07-31   来源:CSDN  作者:yousp
Java的中文问题由来已久,前不久笔者需要做内存中的中文比较排序,对字符串进行GBK或者GB2312编码以后,使用String.compareTo方法仍然不能得到正确结果。因此,怀着怀疑的态度,对JDK中String类的源代码做了一翻探究。(作者使用JDK为1.3.1版本)



以下是String.java中compareTo的源代码,请注意其中的注释:


public class String 

{

       …

    public int compareTo(String anotherString) {

       int len1 = count;

       int len2 = anotherString.count;





       //n为两个字符串长度的最小者

       int n = Math.min(len1, len2);

       

       //获取字符数组

       char v1[] = value;

       char v2[] = anotherString.value;

       

       //取偏依位置

    /** The offset is the first index of the storage that is used. */

       //offset 是第一个存储索引



       int i = offset;

       int j = anotherString.offset;



       //如果i == j

       //这里可能是判断取同一内存中两个字符串的情景。。。

       // A   <--   <----

       // B    s1      |

       // C   <--      |

       // D            s2

       // E            |

       // F            |

       // G   <----------

       // 可能这种情况 i = j

       if (i == j) {

           int k = i;

           int lim = n + i;

             

           while (k < lim)

              {

                     char c1 = v1[k];

                     char c2 = v2[k];

                     

                     if (c1 != c2) file://直到找到一个不相等的字符,返回c1 - c2

                            return c1 - c2;

                     k++;

           }

       } else {

           while (n-- != 0) file://直到两个字符串长度记数为0

              {

                     char c1 = v1[i++]; file://分别取字符

                     char c2 = v2[j++];

                     if (c1 != c2) {  //发现不相等,立即返回c1 - c2;

                            return c1 - c2;

                     }

           }

       }

       return len1 - len2;

//最后这里可能出现的情况是: 两个字符串比较完之后还没有得到结果。相等的情况

    }



}//end of class String







为什么Java在做汉字的CompareTo时比较会有问题呢?通过对compareTo源代码的分析发现,关键在于JDK的compareTo实现是直接使用Char来进行比较的:



char c1 = v1[k];

char c2 = v2[k];



可是当Java使用GB2312编码时,一个对汉字所获取到的Char值却是不规则的,即一个汉字在Java中作为一个char来处理(双字节字符)时,将这样的双字节字符进行强制转换成int类型时,所得到的不是包含了汉字编码顺序的中文内码。可以看一下一组测试数据可以看到其中奥妙:



字符
Char值
Byte[]值
按Byte[]合成的值


25105
[50:46]
[-5046]


29233
[80:82]
[-8082]


21271
[79:79]
[-7979]


20140
[66:87]
[-6687]


22825
[52:20]
[-5220]


23433
[80:78]
[-8078]


38376
[61:59]
[-6159]

A
65
[-65]
[65]

B
66
[-66]
[66]

C
67
[-67]
[67]

D
68
[-68]
[68]




按照中文顺序:“我”字应该在“爱”字后面,因此理论上来讲"我"字的Char值应该比“爱"字的char值要大。可是不知道为什么Java的汉字char(两个byte)->int类型的转换会发生很大偏差。而失去了汉字原本在GBK规范当中,按内码排列好的顺序。但从一个汉字拆分成2个字节的byte[]时,所得到的值并没有打乱GBK编码规定的顺序,因此得到解决问题的思路:将String进行GB2312编码后取得某个汉字获取其Char值时,将汉字拆分成2个字节byte[]再进行计算,从而得到正确的内码。



因此我自己写了下面这样几个函数,基本上解决了汉字比较的问题:

函数包括三个,你可以随意放置到任何类当中作为辅助函数使用(Private Helper)。



n public int compare(String s1, String s2) :主要工作是为比较做一些前期的编码工作可以说是系统的一个外壳。

n public int chineseCompareTo(String s1, String s2):该函数则是中文字符串比较主体,其内部实现了比较的最基本逻辑,和JDK的compareTo所使用的逻辑是一样的。调用接口也一样。

n public static int getCharCode(String s):该函数则负责将一个以字符串形式存在的字符转换成为int编码,儿不损失其位置信息。注意输入通常是:“我”或者“A”,如果输入更长的字符串,则改函数获得的是第一个字符的值。





private static String __ENCODE__ = "GBK"; file://一定要是GBK

private static String __SERVER_ENCODE__ = "GB2312"; file://服务器上的缺省编码

/*

比较两字符串

*/

       public int compare(String s1, String s2)

       {

              String m_s1 = null, m_s2 = null;

              try

              {

                     //先将两字符串编码成GBK

                     m_s1 = new String ( s1.getBytes(__SERVER_ENCODE__), __ENCODE__);

                     m_s2 = new String ( s2.getBytes(__SERVER_ENCODE__), __ENCODE__);

              }

              catch( Exception ex)

              {

                     return s1.compareTo(s2);

              }

              int res = chineseCompareTo(m_s1, m_s2);



              System.out.println("比较:" + s1 + " | " + s2 + "==== Result: " + res);

              return res;

       }



//获取一个汉字/字母的Char值

       public static int getCharCode(String s)

       {

              if (s==null && s.equals(“”)) return -1; file://保护代码

byte [] b = s.getBytes();

              int value = 0;

              //保证取第一个字符(汉字或者英文)

              for (int i = 0; i < b.length && i <= 2; i ++)

              {

                     value = value * 100 + b[i];

              }

              return value;

       }



//比较两个字符串

       public int chineseCompareTo(String s1, String s2)

       {

              int len1 = s1.length();

              int len2 = s2.length();



              int n = Math.min(len1, len2);



              for (int i = 0; i < n; i ++)

              {

                     int s1_code = getCharCode(s1.charAt(i) + "");

                     int s2_code = getCharCode(s2.charAt(i) + "");

                     if (s1_code != s2_code) return s1_code - s2_code;

              }

              return len1 - len2;

       }






可见,对系统源代码的解剖,能让我们在迷惑之余同样有机会窥探系统内部运作的奥妙。不过让人非常费解的是,Java内部的某些类书写风格非常不好,同时存在一些Bug。不过这也许是笔者个人感受。偶有所获,愿与大家共同分享,其中疏漏之处望不吝赐教。

  --相关文章--
· J2EE全面介绍(二) (2007-04-13)
· 项目经验二则:读取war包中的文件及Ant使用中的OutOfMemoryError解决 (2007-04-13)
· 走向J2EE,漫长的道路 (2007-04-13)
· 步入J2EE架构和过程(2) (2007-04-13)
· 步入J2EE架构和过程(1) (2007-04-13)
· 方兴未艾的CORBA (2007-04-13)

版权所有©2005-2006 JAVA资源网 渝ICP备05007591号 虚拟主机 | 关于我们 | 联系方式 | 广告业务 | 网站地图 | 友情链接