在Java中比较UTF-8字符串

5
在我的Java程序中,我正在从XML中检索一些数据。这个XML有一些国际字符,并且采用UTF8编码。现在我使用XML解析器读取此XML。一旦我从XML解析器中检索到特定的国际字符串,我需要将其与预定义的字符串集进行比较。问题是当我在国际字符串上使用string.equals时,比较失败。
如何在Java中比较国际字符串?我正在使用SAXParser和XMLReader从XML中读取字符串。
以下是比较字符串的代码行:
 String country;
 country = getXMLNodeString();

 if(country.equals("Côte d'Ivoire"))
 {    

 } 

  getXMLNodeString()
  {

  /* Get a SAXParser from the SAXPArserFactory. */  
        SAXParserFactory spf = SAXParserFactory.newInstance();
        SAXParser sp = spf.newSAXParser();

        /* Get the XMLReader of the SAXParser we created. */
        XMLReader xr = sp.getXMLReader();
        /* Create a new ContentHandler and apply it to the XML-Reader*/
        XmlParser xmlParser = new XmlParser();  //my class to parse xml
        xr.setContentHandler(xmlParser);  

        /* Parse the xml-data from our URL. */
        xr.parse(new InputSource(url.openStream()));
        /* Parsing has finished. */


       //return string here
  }

2
在支持UTF-8的控制台上执行System.out.println(country)(如果您使用Eclipse,可以通过Window > Preferences > General > Workspace > Text File Encoding配置控制台编码),然后将其粘贴到此处。注意:当您说“国际字符”时,实际上是指"Unicode字符" - BalusC
比较有什么帮助呢? - cppdev
没有错误。但是比较失败了,控制流程没有进入if语句。 - cppdev
如果你使用System.out.println(country),你会得到什么? - Chris J
1
应使用Unicode排序算法比较已解码的Unicode字符串,否则您将受到诸如规范化之类的影响。但是似乎您对编码不太清楚。 - tchrist
4个回答

7
Java将String存储为char数组,这些数组是16位无符号值。这是基于早期支持64K字符的Unicode标准。
您的字符串常量"Côte d'Ivoire"是以此格式存储的。如果XML文档上的字符编码正确,则从中读取的String也将以正确的格式显示。因此可能出现以下错误:
  1. XML文档未声明字符编码;

  2. 声明的字符编码与实际使用的字符编码不匹配。

也许XML字符串被视为US-ASCII而不是UTF-8。我会输出两者并仔细查看它们。如果它们看起来相同,请逐个比较字符,以确定比较失败的位置。您还可以将常量String的UTF8编码与XML文档中的内容进行比较:
byte[] bytes = "Côte d'Ivoire".getBytes("UTF-8");

当你开始涉及到“补充字符”时,情况就变得更加复杂了。这些字符超出了最初意图的64K(Unicode术语中的“代码点”)。请参见Java平台中的补充字符。虽然你使用的任何字符都不会遇到这个问题,但为了完整起见,值得注意一下。


正确!我的 XML 文档没有标记指定字符编码。 默认使用哪种字符编码? - cppdev
@cppdev 但是你需要做的是找出比较失败的地方。逐个字符和逐个字节进行比较,看看差异在哪里。从那里开始,你/我们应该能够弄清楚原因。 - cletus
XML 的默认编码是 UTF-8,如此规定在这里:http://www.w3.org/TR/xml/#charencoding。 - Stijn de Witt
把难以理解的措辞交给W3C处理吧 :) "对于一个既不以字节顺序标记(Byte Order Mark)开头,也没有编码声明(Encoding declaration),却使用除UTF-8外的编码方式来表示内容是一个致命错误。请注意,由于ASCII是UTF-8的子集,普通的ASCII实体并不严格需要编码声明。" - Stijn de Witt
换句话说:如果没有声明编码,编码必须是UTF-8(或ASCII,它是UTF-8的子集),否则将被视为错误。 - Stijn de Witt
显示剩余2条评论

3

由于您正在与字符串文字进行比较,因此您需要确保将源文件保存在与javac期望的相同编码中。您还可以使用-encoding参数指定源文件的编码方式。

这似乎是此情况下最可能出现问题的地方。

请注意,我所说的是Java源代码的编码方式,而不是XML文档的编码方式。


好的,这确实可能会让你受到影响!Eclipse默认情况下会将.java文件保存为特定于平台的(例如ISO-8859-1)格式!我在我的博客上描述了如何更改它:http://stijndewitt.wordpress.com/2010/05/05/unicode-utf-8-in-eclipse-java/ - Stijn de Witt

2

Java字符串始终为UTF-16编码。在读取时,您的XML解析器应将文件中的UTF-8字符转换为UTF-16,并且您自己的字符串已经以UTF-16形式存储在内存中,因此可以使用普通的equals()方法进行比较。如果它们在您认为它们应该相等的时候没有比较相等,则问题很可能是其他方面引起的。


0
如果您的XML文件被标记为,并且文本文件保存为实际的UTF-8文件,那么您可以像这样使用contentEquals(文字或字符串):
if (strMyvalue.contentEquals("Côte d'Ivoire") {
    // execute
}

不,你不能在Unicode字符串上执行contentEquals并期望得到合理的结果。它们可能具有相同的文本,但不是按位等效的。你忘记了规范化等其他事情。Java在Unicode方面表现不佳,因为它没有适当的Unicode字符串类。考虑Co\x{302}te d\x{2019}IvoireC\x{F4}te d\x{2019}Ivoire,更不用说C\x{F4}te d'Ivoire了。它们都有相同的字母,而且第一对甚至是规范等效的。不要像你所建议的那样信任非文本方法来处理Unicode文本。 - tchrist

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接