不区分大小写的字符串比较奇怪的行为

时间:2018-10-12 10:31:30

标签: java c# .net unicode

这在C#和Java中都发生,所以我认为这不是错误,只是想知道为什么。

var s = "";
var lower = s.ToLower();
var upper = s.ToUpper();

if (!lower.Equals(upper, StringComparison.OrdinalIgnoreCase))
{
    //How can this happen?
}

根据this page,“”的小写字母是“”,与IgnoreCase选项比较时,它们应相等。为什么它们不相等?

2 个答案:

答案 0 :(得分:0)

为捍卫Java API:documentation of the method String.equalsIgnoreCase从未宣称它将在任意Unicode代码点上“按预期”工作。它说:

  

如果两个字符c1和c2位于   以下至少一项是正确的:

     
      
  • 两个字符相同(与==运算符相比)
  •   
  • 将Character.toUpperCase(char)方法应用于每个字符会产生相同的结果
  •   
  • 将Character.toLowerCase(char)方法应用于每个字符会产生相同的结果
  •   

因此,文档明确指出,它将Character.toUpperCase应用于char,即应用于UTF-16 代码单元,而不应用于Unicode代码点。 / p>

如果在每个代码 point 上使用方法Character.toUpperCase(int codePoint),则比较的行为符合预期。这是Scala中的一个简短示例(使用完全相同的Java API,希望使用高阶forall方法是不言自明的):

val a = ""
val b = ""
(a.codePoints.toArray zip b.codePoints.toArray).forall { 
  case (x, y) => 
  Character.toLowerCase(x) == Character.toLowerCase(y) 
}

打印

true

符合预期。为什么会这样呢?我认为可以将这一点归咎于向后兼容性。

答案 1 :(得分:0)

将“”和“”都转换为它们的数值时,您可能会得到更有趣的值。您获得了相同的55297整数值作为转换结果。 StringComparison.Ordinal基于字符的数值。由于“序数”表示“基于数字”,并且两个字符(大写和小写)在转换后都具有相同的序数值,因此任何“基于数字”的比较都会提供意外的结果。显然,没有为大写和小写字符具有相同“常规”值的字符定义OrdinalIgnoreCase,以避免出现意外结果,并且在比较此类字符时会失败(即导致错误)。