【问题标题】:someString.IndexOf(someString) returns 1 instead of 0 under .NET 4someString.IndexOf(someString) 在 .NET 4 下返回 1 而不是 0
【发布时间】:2012-07-13 02:57:50
【问题描述】:

我们最近将所有项目从 .NET 3.5 升级到 .NET 4。关于string.IndexOf(),我遇到了一个相当奇怪的问题。

我的代码显然做了一些稍微不同的事情,但在调查问题的过程中,我发现在一个字符串上调用 IndexOf() 本身返回 1 而不是 0。 换句话说:

string text = "\xAD\x2D";          // problem happens with "­-dely N.China", too;
int index = text.IndexOf(text);    // see update note below.

给我的索引是 1,而不是 0。关于这个问题需要注意的几点:

  • 问题似乎与这些连字符有关(第一个字符是 Unicode 软连字符,第二个是常规连字符)。

  • 我已经仔细检查过,这在 .NET 3.5 中不会发生,但在 .NET 4 中会发生。

  • IndexOf() 更改为进行序数比较可解决问题,因此由于某种原因,默认IndexOf 会忽略第一个字符。

有人知道为什么会这样吗?

编辑

对不起,伙计们,在原始帖子上做了一些东西,并在那里两次获得了隐藏的破折号。我已经更新了字符串,只要将它粘贴到正确的编辑器中,它应该返回 1 而不是 2 的索引。

更新:

将原始问题字符串更改为每个实际字符都清晰可见的字符串(使用转义)。这稍微简化了问题。

【问题讨论】:

  • Mono 2.10.8.1(兼容.net 4)返回0...
  • 您的本地默认区域/文化设置是什么?
  • @sll 程序是 en-GB 的,但由于我是在将字符串与自身进行比较,会有所不同吗?

标签: c# string .net-4.0 .net-3.5


【解决方案1】:

您的字符串包含两个字符:soft hyphen(Unicode 代码点 173)和 hyphen(Unicode 代码点 45)。

Wiki:根据 Unicode 标准,如果该点没有断行,则不会显示软连字符。

在 .NET 4 中使用 "\xAD\x2D".IndexOf("\xAD\x2D") 时,似乎忽略了您正在寻找软连字符,返回起始索引 1(\x2D 的索引)。在 .NET 3.5 中,这将返回 0。

如果你运行这段代码会更有趣(所以当寻找软连字符时):

string text = "\xAD\x2D";
string shy = "\xAD";
int i1 = text.IndexOf(shy);

然后i1 变为 0,无论使用什么 .NET 版本。 text.IndexOf(text); 的结果确实不同,乍一看对我来说就像一个错误。

据我所知,较早的 .NET 版本使用 InternalCallIndexOfString()(我不知道该调用哪个 API),而从 .NET 4 开始使用 @987654325 @ 到 InternalFindNLSStringEx() 被创建,然后调用 FindNLSStringEx()

调用FindNLSStringEx时确实出现了这个问题(我真的不知道这是否是预期的行为):

LPCWSTR lpStringSource = L"\xAD\x2D";
LPCWSTR lpStringValue = L"\xAD";

int length;

int i = FindNLSStringEx(
    LOCALE_NAME_SYSTEM_DEFAULT,
    FIND_FROMSTART,
    lpStringSource,
    -1,
    lpStringValue,
    -1,
    &length,
    NULL,
    NULL,
    1);

Console::WriteLine(i);

i = FindNLSStringEx(
    LOCALE_NAME_SYSTEM_DEFAULT,
    FIND_FROMSTART,
    lpStringSource,
    -1,
    lpStringSource,
    -1,
    &length,
    NULL,
    NULL,
    1);

Console::WriteLine(i);

Console::ReadLine();

先打印0再打印1。注意length,一个表示找到的字符串长度的out参数,第一次调用后为0,第二次调用后为1;软连字符被计为长度为 0。

如您所述,解决方法是使用text.IndexOf(text, StringComparison.OrdinalIgnoreCase);。这会向InternalCompareStringOrdinalIgnoreCase() 发起一个QCall,后者又调用FindStringOrdinal(),这两种情况都返回0。

【讨论】:

  • @Tigran 当我将您的代码直接粘贴到 Visual Studio(即支持 Unicode 的编辑器)中时,.NET 4 的 String.IndexOf() 返回 2
  • 我认为 OP 也使用 VS,听起来很奇怪,因为他明确声明了字符串由 char(173) + char(45) + 提醒组成的事实。确实,如果您 复制/粘贴 它会显示更多字符,但是如果您在 OP 声明之后生成一个字符串,它会返回 1。 Si I 认为该字符串显示在这里的页面 不是 一个 OP 要求的,最好根据他的建议生成一个字符串。
  • 对不起,请检查新编辑的版本,应该返回1。
  • 但至于你的回答......你是在告诉我 IndexOf 忽略破折号是因为它不会显示吗?
  • 好的,除非另有说明,否则我会将其视为意外行为。感谢所有的努力,CodeCaster。
【解决方案2】:

这似乎是 .NET4 中的一个错误,.NET4 Beta1 中的新更改恢复到与 .NET 2.0/3.0/3.5 相同的先前版本。

What's New in the BCL in .NET 4.0 CTP(MSDN 博客)

.NET 4 中的字符串安全性更改

默认情况下,System.String 上的部分匹配重载(StartsWith、EndsWith、IndexOf 和 LastIndexOf)已更改为与区域性无关(序数)。

此更改影响String.IndexOf 方法的行为,默认情况下将它们更改为执行序数(逐字节)比较,并将更改为使用CultureInfo.InvariantCulture 而不是CultureInfo.CurrentCulture

.NET 4 Beta 1 更新

为了保持 .NET 4 与以前版本之间的高度兼容性,我们决定恢复此更改。 String 的默认部分匹配重载以及 String 和 Char 的 ToUpper 和 ToLower 方法的行为现在与它们在 .NET 2.0/3.0/3.5 中的行为相同。 .NET 4 Beta 1 中存在对原始行为的更改。


To fix this,将字符串比较方法更改为接受System.StringComparison 枚举作为参数的重载,并指定OrdinalOrdinalIgnoreCase

// string contains 'unicode dash' \x2D
string text = "\xAD\x2D"; 

// woks in .NET 2.0/3.0/3.5 and .NET 4 Beta 1 and later
// but seems be buggy in .NET 4 because of 'culture-sensitive' comparison        
int index = text.IndexOf(text); 

// fixed version
index = text.IndexOf(text, StringComparison.Ordinal); 

【讨论】:

  • 请将您链接到的帖子的简短摘要直接添加到答案中。参考来源总是很棒,但是任何 SO 答案都应该有意义,而不必遵循外部链接。
  • 所以从你引用的内容来看,他们没有改变任何东西,重大变化-链接已经过时了。
  • 感谢您将此详细信息添加到主题中。很有帮助。
【解决方案3】:

来自documentation(我的重点):

此方法使用当前区域性执行单词(区分大小写和区域性)搜索。

即。一些不同的代码点将被视为平等。

如果您使用带有StringComparison 值并传递StringComparison.Ordinal 的重载来避免文化依赖性,会发生什么情况?

【讨论】:

  • 如帖子中所述,更改为 Ordinal 确实可以解决问题,但我不明白为什么。由于您正在将字符串与其自身进行比较,因此它应该具有相同的文化设置,因此它是否进行区分大小写和区分文化的搜索并不重要?
  • @knersis 文化相关的比较会改变许多比较的结果。例如。在土耳其语言环境中,Ii 的不区分大小写比较是不相等。在该文化的核心字符集之外,字符通常被忽略。 (在英语中,这包括口音)。这是一个复杂的领域,我只了解一点。通过Michael Kaplan's blog 拖网可能是最简单的路线。几年前,他解释了该过程在 Win32(.NET 使用)中的工作原理。
猜你喜欢
  • 1970-01-01
  • 2012-01-05
  • 1970-01-01
  • 1970-01-01
  • 2022-12-16
  • 2017-04-03
  • 2016-06-12
  • 2016-05-06
  • 1970-01-01
相关资源
最近更新 更多