C#"anyString".Contains('',StringComparison.InvariantCulture)在.NET5中返回true但在旧版本中返回false

c#

c#-9.0

我在尝试将项目从 .NET core 3.1 升级到最新的 .NET 5 时遇到了不兼容的问题。

我的原始代码有一个验证逻辑,通过检查从 Path.GetInvalidFileNameChars() API 返回的每个字符来检查无效的文件名字符。


var invalidFilenameChars = Path.GetInvalidFileNameChars();
bool validFileName = !invalidFilenameChars.Any(ch => fileName.Contains(ch, StringComparison.InvariantCulture));

假设您为 fileName 提供了一个常规值,例如应该有效的“test.txt”。然而,令人惊讶的是,如果您使用“net5”目标框架运行,上面的代码给出的文件名无效。

在调试了一段时间后,我发现返回的无效字符集包含 ''、空 ASCII 字符和 "text.txt".Contains(", StringComparison.InvariantCulture) 为真。

    class Program
    {
        static void Main(string[] args)
        {
            var containsNullChar = "test".Contains("", StringComparison.InvariantCulture);
    
            Console.WriteLine($"Contains null char {containsNullChar}");
        }
    }

如果您在 .NET core 3.1 中运行,它永远不会说常规字符串包含空字符。此外,如果我省略第二个参数 (StringComparison.InvariantCulture) 或者如果我使用 StringComparison.Ordinal,则永远不会返回奇怪的结果。

为什么在 .NET5 中改变了这种行为?

编辑:
正如 Karl-Johan Sjögren 之前所评论的,.NET5 中确实存在关于字符串比较的行为变化:

在 .NET 5+ 上比较字符串时的行为变化

另请参阅相关票证:

string.IndexOf 在 .Net 5 中得到不同的结果

虽然这个问题应该与上面有关,但与 '' 相关的当前结果对我来说仍然很奇怪,并且可能仍然被认为是@xanatos 回答的错误。

编辑2:

现在我意识到这个问题的真正原因是我在 InvariantCulture 和 Ordinal 字符串比较之间的混淆。它们实际上是完全不同的东西。看下面的票:

InvariantCulture 和 Ordinal 字符串比较之间的区别

另请注意,这应该是 .NET 的独特问题,因为其他主要编程语言(例如 Java、C++ 和 Python)默认处理序数比较。

回答

不是错误,是功能

这 问题,我已经打开已经关闭,但他们给了一个很好的解释。现在...在 .NET 5.0 中,他们开始在 Windows 上使用(在 Linux 上已经存在)一个用于比较字符串的新库,即 ICU 库。它是Unicode Consortium的官方库,所以是“动词”。该库用于CurrentCulture, InvariantCulture(加上各自的IgnoreCase)和任何其他文化。唯一的例外是Ordinal/ OrdinalIgnoreCase。该库是针对文本的,它有一些关于非文本的“特殊”想法。在这种特殊情况下,有些字符被简单地忽略了. 在块0000-00FF我想说忽略的人物都是控制代码(请忽略它们显示为这样的事实€‚ƒ„†‡ˆ‰Š‹ŒŽ‘’“”•–—™š›œžŸ,在一定的程度后,这些字符已重新映射,其他地方以Unicode,但显示的glyps不反映它,但是如果您尝试查看他们的代码,就像这样做一样,char ch = '€'; int val = (int)ch;您会看到它),并且''是一个控制代码。

现在...我个人的想法是比较 string从今天,您需要拥有 Unicode 技术硕士学位,我确实希望他们会在 .NET 6.0 中做一些恶作剧来进行默认比较Ordinal(这是其中之一).NET 6.0 的建议,选项 B)。请注意,如果您想制作可以在土耳其运行的程序,您已经需要 Unicode 技术硕士学位(请参阅土耳其语 i 问题)。

一般来说,我会说要查找不是关键字/固定词(例如列名)的词,您应该使用文化感知比较,同时查找关键字/固定词(例如列名)和符号/您应该使用序数比较的控制代码。问题是当您想同时查找两者时。通常在这种情况下,您正在寻找确切的单词,因此您可以使用序数。否则就变成地狱了。我什至不想考虑 Regex 在文化感知环境中如何在内部工作。我不想去想。因为在那个方向上只能是愚蠢和噩梦。

作为旁注,即使在“默认”文化感知比较之前,也有一些秘密的 shaeaningans ......例如:

int ix = "?$?".IndexOf("$"); // -1 on .NET Framework or .NET Core <= 3.1

我之前写的

我会说这是一个错误。有一个类似的错误IndexOf。我已经在 github 上打开了一个问题来跟踪它。

正如你所写的, OrdinalOrdinalIgnoreCase按预期工作(可能是因为它们不需要使用新的 ICU 库来处理 Unicode)。

一些示例代码:

Console.WriteLine($"Ordinal Contains null char {"test".Contains("", StringComparison.Ordinal)}");
Console.WriteLine($"OrdinalIgnoreCase Contains null char {"test".Contains("", StringComparison.OrdinalIgnoreCase)}");

Console.WriteLine($"CurrentCulture Contains null char {"test".Contains("", StringComparison.CurrentCulture)}");
Console.WriteLine($"CurrentCultureIgnoreCase Contains null char {"test".Contains("", StringComparison.CurrentCultureIgnoreCase)}");

Console.WriteLine($"InvariantCulture Contains null char {"test".Contains("", StringComparison.InvariantCulture)}");
Console.WriteLine($"InvariantCultureIgnoreCase Contains null char {"test".Contains("", StringComparison.InvariantCultureIgnoreCase)}");

Console.WriteLine($"Ordinal IndexOf null char {"test".IndexOf("t", StringComparison.Ordinal)}");
Console.WriteLine($"OrdinalIgnoreCase IndexOf null char {"test".IndexOf("", StringComparison.OrdinalIgnoreCase)}");

Console.WriteLine($"CurrentCulture IndexOf null char {"test".IndexOf("", StringComparison.CurrentCulture)}");
Console.WriteLine($"CurrentCultureIgnoreCase IndexOf null char {"test".IndexOf("", StringComparison.CurrentCultureIgnoreCase)}");

Console.WriteLine($"InvariantCulture IndexOf null char {"test".IndexOf("", StringComparison.InvariantCulture)}");
Console.WriteLine($"InvariantCultureIgnoreCase IndexOf null char {"test".IndexOf("", StringComparison.InvariantCultureIgnoreCase)}");

Console.WriteLine($"Ordinal Contains null char {"test".Contains("test", StringComparison.Ordinal)}");
Console.WriteLine($"OrdinalIgnoreCase Contains null char {"test".Contains("test", StringComparison.OrdinalIgnoreCase)}");

Console.WriteLine($"CurrentCulture Contains null char {"test".Contains("test", StringComparison.CurrentCulture)}");
Console.WriteLine($"CurrentCultureIgnoreCase Contains null char {"test".Contains("test", StringComparison.CurrentCultureIgnoreCase)}");

Console.WriteLine($"InvariantCulture Contains null char {"test".Contains("test", StringComparison.InvariantCulture)}");
Console.WriteLine($"InvariantCultureIgnoreCase Contains null char {"test".Contains("test", StringComparison.InvariantCultureIgnoreCase)}");

Console.WriteLine($"Ordinal IndexOf null char {"test".IndexOf("t", StringComparison.Ordinal)}");
Console.WriteLine($"OrdinalIgnoreCase IndexOf null char {"test".IndexOf("test", StringComparison.OrdinalIgnoreCase)}");

Console.WriteLine($"CurrentCulture IndexOf null char {"test".IndexOf("test", StringComparison.CurrentCulture)}");
Console.WriteLine($"CurrentCultureIgnoreCase IndexOf null char {"test".IndexOf("test", StringComparison.CurrentCultureIgnoreCase)}");

Console.WriteLine($"InvariantCulture IndexOf null char {"test".IndexOf("test", StringComparison.InvariantCulture)}");
Console.WriteLine($"InvariantCultureIgnoreCase IndexOf null char {"test".IndexOf("test", StringComparison.InvariantCultureIgnoreCase)}");


以上是C#"anyString".Contains('',StringComparison.InvariantCulture)在.NET5中返回true但在旧版本中返回false的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>