【问题标题】:Case-insenstive string comparison strange behavior不区分大小写的字符串比较奇怪的行为
【发布时间】:2019-03-17 13:44:50
【问题描述】:

这发生在 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 选项比较时它们应该相等。为什么他们不相等?

【问题讨论】:

  • @AccessDenied 原题中的测试用例绰绰有余,够荒谬
  • 你用的是什么java代码?
  • 幕后工作原理:第 697 行 GlobalizationNative_CompareStringOrdinalIgnoreCase github.com/dotnet/coreclr/blob/…
  • 这就是为什么它在 Java 中的工作方式相同。它使用相同的库icu-project.org

标签: java c# .net unicode


【解决方案1】:

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

两个字符 c1 和 c2 被认为是相同的忽略大小写,如果 以下至少一项是正确的:

  • 这两个字符相同(通过 == 运算符比较)
  • 对每个字符应用 Character.toUpperCase(char) 方法会产生相同的结果
  • 对每个字符应用 Character.toLowerCase(char) 方法会产生相同的结果

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

如果您对每个代码 使用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

正如预期的那样。这是为什么?我认为可以放心地将此归咎于向后兼容性。

【讨论】:

    【解决方案2】:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-05-27
      • 1970-01-01
      • 1970-01-01
      • 2023-03-22
      • 2011-01-09
      相关资源
      最近更新 更多