不是Bug,而是特性
我打开的问题已经关闭了,但是他们给出了一个非常好的解释。现在... 在.NET 5.0中,他们开始在Windows上(在Linux上已经存在)使用新的字符串比较库,即ICU库。它是Unicode联盟的官方库,因此它是“动词”。该库用于CurrentCulture
,InvariantCulture
(加上相应的IgnoreCase
)和任何其他语言环境。唯一的例外是Ordinal
/OrdinalIgnoreCase
。该库的目标是文本,对于非文本有一些“特别”的想法。在这种情况下,有一些字符被简单地忽略掉了。在0000-00FF块中,我会说被忽略的字符都是控制字符(请忽略它们显示为€‚ƒ„†‡ˆ‰Š‹ŒŽ‘’“”•–—™š›œžŸ
这一事实,在某个时候,这些字符已经被重新映射到Unicode的其他位置,但是显示的字形没有反映出来,但是如果你尝试查看它们的代码,比如做char ch = '€'; int val = (int)ch;
,你会看到它),而'\0'
则是一个控制字符。
现在... 我个人认为,如果要比较今天的string
,你需要一个Unicode技术硕士学位,我确实希望他们会在.NET 6.0中进行一些花招,使默认比较成为Ordinal
(这是.NET 6.0的建议之一,即选项B)。请注意,如果您想制作可以在土耳其运行的程序,您已经需要一个Unicode技术硕士学位(请参见土耳其i问题)。
一般而言,若要查找非关键字/固定字词(例如列名称)的词汇,应使用区域性敏感比较;若要查找关键字/固定字词(例如列名称)和符号/控制码,则应使用基数比较。问题在于,当您想同时查找两者时。通常情况下,您正在寻找
确切的词汇,因此可以使用基数比较。否则,就会变得非常困难。我甚至不想考虑在区域性敏感环境中正则表达式的内部工作方式。那是我不想去想的事情。因为在那个方向上只可能出现愚蠢和噩梦。
顺便提一下,在“默认”的区域性敏感比较之前,有一些秘密交易...例如:
int ix = "ʹ$ʹ".IndexOf("$"); // -1 on .NET Framework or .NET Core <= 3.1
我之前所写的内容
我会说这是一个错误。在IndexOf
中也存在类似的 bug. 我在 Github 上开了一个 Issue 来跟踪它。
正如你所写的,Ordinal
和 OrdinalIgnoreCase
的工作效果是符合预期的(可能是因为它们不需要使用新的 ICU 库来处理 Unicode)。
一些示例代码:
Console.WriteLine($"Ordinal Contains null char {"test".Contains("\0", StringComparison.Ordinal)}");
Console.WriteLine($"OrdinalIgnoreCase Contains null char {"test".Contains("\0", StringComparison.OrdinalIgnoreCase)}");
Console.WriteLine($"CurrentCulture Contains null char {"test".Contains("\0", StringComparison.CurrentCulture)}");
Console.WriteLine($"CurrentCultureIgnoreCase Contains null char {"test".Contains("\0", StringComparison.CurrentCultureIgnoreCase)}");
Console.WriteLine($"InvariantCulture Contains null char {"test".Contains("\0", StringComparison.InvariantCulture)}");
Console.WriteLine($"InvariantCultureIgnoreCase Contains null char {"test".Contains("\0", StringComparison.InvariantCultureIgnoreCase)}");
Console.WriteLine($"Ordinal IndexOf null char {"test".IndexOf("\0t", StringComparison.Ordinal)}");
Console.WriteLine($"OrdinalIgnoreCase IndexOf null char {"test".IndexOf("\0", StringComparison.OrdinalIgnoreCase)}");
Console.WriteLine($"CurrentCulture IndexOf null char {"test".IndexOf("\0", StringComparison.CurrentCulture)}");
Console.WriteLine($"CurrentCultureIgnoreCase IndexOf null char {"test".IndexOf("\0", StringComparison.CurrentCultureIgnoreCase)}");
Console.WriteLine($"InvariantCulture IndexOf null char {"test".IndexOf("\0", StringComparison.InvariantCulture)}");
Console.WriteLine($"InvariantCultureIgnoreCase IndexOf null char {"test".IndexOf("\0", StringComparison.InvariantCultureIgnoreCase)}");
and
Console.WriteLine($"Ordinal Contains null char {"test".Contains("\0test", StringComparison.Ordinal)}");
Console.WriteLine($"OrdinalIgnoreCase Contains null char {"test".Contains("\0test", StringComparison.OrdinalIgnoreCase)}");
Console.WriteLine($"CurrentCulture Contains null char {"test".Contains("\0test", StringComparison.CurrentCulture)}");
Console.WriteLine($"CurrentCultureIgnoreCase Contains null char {"test".Contains("\0test", StringComparison.CurrentCultureIgnoreCase)}");
Console.WriteLine($"InvariantCulture Contains null char {"test".Contains("\0test", StringComparison.InvariantCulture)}");
Console.WriteLine($"InvariantCultureIgnoreCase Contains null char {"test".Contains("\0test", StringComparison.InvariantCultureIgnoreCase)}");
Console.WriteLine($"Ordinal IndexOf null char {"test".IndexOf("\0t", StringComparison.Ordinal)}");
Console.WriteLine($"OrdinalIgnoreCase IndexOf null char {"test".IndexOf("\0test", StringComparison.OrdinalIgnoreCase)}");
Console.WriteLine($"CurrentCulture IndexOf null char {"test".IndexOf("\0test", StringComparison.CurrentCulture)}");
Console.WriteLine($"CurrentCultureIgnoreCase IndexOf null char {"test".IndexOf("\0test", StringComparison.CurrentCultureIgnoreCase)}");
Console.WriteLine($"InvariantCulture IndexOf null char {"test".IndexOf("\0test", StringComparison.InvariantCulture)}");
Console.WriteLine($"InvariantCultureIgnoreCase IndexOf null char {"test".IndexOf("\0test", StringComparison.InvariantCultureIgnoreCase)}");
"test".IndexOf("\0", StringComparison.InvariantCulture) == 0
而不是 -1 或 4。 - xanatos