无法在字符串中搜索换行符

3
在C#中,我正在寻找字符串内部的索引位置 - 具体来说是换行符(\n)所在的索引位置。
给定一个带有Windows换行符(\r\n)的字符串:
如果我查找"\n",它会返回-1。如果我查找"\r\n",我得到一个结果。如果我将'\n'作为字符查找,则会得到一个结果。
给定一个带有Unix换行符(\n)的字符串,我会得到一个结果。
string s = "hello\r\nworld";

Console.WriteLine(@"\r\n index: " + s.IndexOf("\r\n")); // 5
Console.WriteLine(@"\n index as string: " + s.IndexOf("\n")); // -1
Console.WriteLine(@"\n index as char: " + s.IndexOf('\n')); // 6


s = "hello\nworld";

Console.WriteLine(@"\n index as string: " + s.IndexOf("\n")); // 5
Console.WriteLine(@"\n index as char: " + s.IndexOf('\n')); // 5

我知道换行符占两个字符,如果我使用StreamReader,File.ReadAllLines或类似的东西,那么它会被自动处理并且我会失去它们。

我以为\n本身就是一个有效的字符串,而\r\n虽然很特别,但在字符串中仍表示为两个不同的字符。但这告诉我另外一种情况。

我可以对字符进行IndexOf操作而不是字符串('\n'而不是"\n"),但我真的想知道为什么会发生这种情况,以便我可以计划它。

编辑

提示:刚刚发现将字符串转换为Span可得到正确的结果。不确定其中涉及的开销,因此不知道与Ordinal解决方案相比如何-我猜Ordinal是更好的解决方案:

Console.WriteLine(@"\n index as string Ordinal: " 
    + s.IndexOf("\n", StringComparison.Ordinal)); // 6

Console.WriteLine(@"\n index as Span: "
    + s.AsSpan().IndexOf("\n".AsSpan())); // 6

Console.WriteLine(@"\n index as string with s.AsSpan(): " 
    + s.AsSpan().IndexOf("\n")); // 6

我运行了你的代码,得到了不同的结果。行索引为:5 6 6 5 5。 - Nigel
@OmarAbdelBari 嗯,这很好知道 - 在我的情况下,我试图保留我正在查看的数据的换行符,所以我不想使用系统分隔符,以防特定文件中有一些类Unix的结尾 - 我只是在尝试计算一堆文件中的换行符时注意到了这一点。 - Joe Enos
1
@NigelBess 这真的很有趣 - 我刚刚将我的项目从.NET 5更改为.NET Core 3.1,结果就像你的一样是5 6 6 5 5 - 我猜你是在3.1上运行的?也许这是一个框架错误(或者是一个没有得到关注的功能)。如果我找不到答案,我会向Microsoft提交此问题。 - Joe Enos
@Joe Enos 我实际上在 .NET 6 预览版 7 上运行了它,哈哈。 - Nigel
@JoeEnos 我刚把项目改成了.NET 5,结果和你一样。第二个输出为-1。 - Nigel
@NigelBess 太棒了 - 他们一定已经在 .NET 6 中将行为改回来了(我没有安装它,所以无法在我的机器上检查)。我看到了 NetMage 的答案和链接,从未听说过这个。 - Joe Enos
2个回答

7
.Net 5.0 在全球化 Windows 库方面进行了更改。以前的版本在 Windows 上使用 NLS,在 Unix 上使用 ICU。 .Net 5 使用 ICU 做到跨平台开发一致,但这会令 Windows 开发人员惊讶(叹息)。由于此更改,您必须传递 StringComparison.Ordinal 来查找字符串中的换行符。
需要注意的是,这也可能取决于 Windows 的版本(叹气&叹气),因为 Windows 10 May 2019 包括 ICU 库,而早期版本则会导致 .Net 5 回退到 NLS。
请参阅来自微软的这篇文章这篇文章详细介绍了受影响的 API。

这太疯狂了 - 看起来那不是一个很好的想法。我喜欢文章几乎与我的示例完全相同。我发誓我没有抄袭 :) - Joe Enos
很不幸,许多API也受到了影响。我认为不幸的是,他们进行了一个全球化变更,而这个变更是选择退出而不是选择加入。如果您不需要/想要处理全球化,您仍然会受到惩罚。也许他们应该将一个参数IndexOf的默认值改为Ordinal,并破坏在Unix上的.Net兼容性。 - NetMage
1
那不是疯狂,那是愚蠢。如果我想要那个,我会想要 StringComparison.Magic - Christian Gollhardt
@ChristianGollhardt StringComparison.LetMeTellYouWhatIThinkYouWantRatherThanWhatYouToldMeToDo - Joe Enos

0

您可以在脚本中使用System.Environment.NewLine,它是一个有条件的属性,根据操作系统而定的换行符。请查看这里

在Windows上:"\r\n"
在Unix平台上:"\n"

using System;
string s = "hello" + Environment.NewLine + "world";

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