【问题标题】:string.IndexOf returns different value in .NET 5.0string.IndexOf 在 .NET 5.0 中返回不同的值
【发布时间】:2020-11-14 11:58:46
【问题描述】:

当我在 .NET Core 3.1 中运行以下代码时,我得到 6 作为返回值。

// .NET Core 3.1
string s = "Hello\r\nworld!";
int idx = s.IndexOf("\n");
Console.WriteLine(idx);

结果:

6

但是当我在 .NET 5.0 中运行这段代码时,我得到了不同的结果。为什么会这样?

// .NET 5.0
string s = "Hello\r\nworld!";
int idx = s.IndexOf("\n");
Console.WriteLine(idx);

结果:

-1

【问题讨论】:

  • 看看this issue
  • 这个例子是docs.microsoft.com/en-us/dotnet/standard/… 的精确副本吗?那我不明白这个问题,因为它在那里解释了为什么以及如何恢复到旧的行为。
  • 这完全取决于使用的 CultureInfo。 NetCore GitHub 存储库中有详细记录的差异。
  • @John:实际上在不指定比较的情况下使用IndexOf 一直是个定时炸弹。如果与不同的区域设置一起使用,即使使用 pre-.NET 5 也可能会失败。我希望我知道谁的想法是默认使用当前文化而不是使用序数比较......与ToStringParse 相同,但没有指定文化。几乎所有关于 SO 的浮点格式化/解析问题都与这个决策问题有关。

标签: c# .net-core .net-5


【解决方案1】:

cmets 和@Ray 的回答包含了原因。

虽然破解 .csprojruntimeconfig.json 文件可能会节省您的时间,但真正的解决方案是明确指定比较:

// this returns the expected result
int idx = s.IndexOf("\n", StringComparison.Ordinal);

由于某种原因,IndexOf(string) 默认使用当前文化比较,当您的应用在与您的区域设置不同的环境中执行时,即使使用早期的 .NET 版本也可能会导致意外。

使用特定文化搜索实际上是一种非常罕见的场景(例如,可以在浏览器、图书阅读器或 UI 搜索中有效),而且它比有序搜索慢得多。

同样的问题适用于StartsWith/EndsWith/Contains/ToUpper/ToLower 甚至ToStringParse 格式化类型的方法(尤其是在使用浮点类型时)这些也默认使用当前的文化,这可能是许多陷阱的来源。但是,如果您不使用特定的比较或文化,最近的代码分析器(例如 FxCop、ReSharper)会警告您。建议在产品代码中为这些问题设置高严重性。

【讨论】:

    【解决方案2】:

    您的示例代码与MSDN 上发布的代码完全一致,该代码还描述了为什么以及如何在这些摘录中恢复旧行为(重点是我的):

    过去,.NET 全球化 API 在不同平台上使用不同的底层库。在 Unix 上,API 使用 International Components for Unicode (ICU),而在 Windows 上,它们使用 National Language Support (NLS)。 [...] 这些领域的行为差异很明显

    • 文化和文化数据
    • 字符串套管
    • 字符串排序和搜索
    • 排序键
    • 字符串规范化
    • 国际化域名 (IDN) 支持
    • Linux 上的时区显示名称

    要恢复使用 NLS [与 Windows 2019 年 5 月 10 日更新和现在默认使用 ICU 的更新版本相关],开发人员可以选择退出 ICU 实施。应用程序可以通过以下任何一种方式启用 NLS 模式:

    • 在项目文件中:

      <ItemGroup>
        <RuntimeHostConfigurationOption Include="System.Globalization.UseNls" Value="true" />
      </ItemGroup>
      
    • runtimeconfig.json 文件中:

      {
        "runtimeOptions": {
           "configProperties": {
             "System.Globalization.AppLocalIcu": "<suffix>:<version> or <version>"
           }
        }
      }
      
    • 通过将环境变量DOTNET_SYSTEM_GLOBALIZATION_APPLOCALICU 设置为值&lt;suffix&gt;:&lt;version&gt;&lt;version&gt;

      &lt;suffix&gt;:长度少于 36 个字符的可选后缀,遵循公共 ICU 打包约定。在构建自定义 ICU 时,您可以对其进行自定义以生成库名称和导出的符号名称以包含后缀,例如 libicuucmyapp,其中 myapp 是后缀。

      &lt;version&gt;:有效的 ICU 版本,例如 67.1。此版本用于加载二进制文件并获取导出的符号。

    有关更多/最新信息,请参阅上面的 MSDN 链接。

    不过,我建议您也阅读György Kőszeg's answer,因为您只需从一开始就担心不精确的字符串操作的这些细节。

    【讨论】:

      猜你喜欢
      • 2013-06-26
      • 2021-08-02
      • 2021-10-07
      • 2022-08-18
      • 2010-10-10
      • 1970-01-01
      • 2021-06-22
      • 1970-01-01
      • 2014-10-30
      相关资源
      最近更新 更多