someString.IndexOf(someString)在.NET 4下返回1而不是0

我们最近把我们所有的项目从.NET 3.5升级到了.NET 4.我遇到了string.IndexOf()方面相当奇怪的问题。

我的代码显然做了一些稍微不同的事情,但在调查问题的过程中,我发现调用IndexOf()的string本身返回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忽略。

有谁知道为什么发生这种情况?

编辑

对不起,伙计们在原帖上写了一些东西,并在那里得到了两次隐藏的冲刺。 我已经更新了string,这应该返回索引1而不是2,只要您将其粘贴到正确的编辑器。

更新:

将原始问题string更改为每个实际字符清晰可见(使用转义)。 这简化了一些问题。

您的string存在两个字符:一个软连字符 (Unicode代码点173)和一个连字符 (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); 

那么无论使用的是什么.NET版本, i1变成0。 text.IndexOf(text); 确实不尽相同,这对我来说看起来像是一个错误。

至于我可以通过框架追溯,旧的.NET版本使用InternalCall IndexOfString() (我不知道哪个API调用去),而从.NET 4 QCall到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.请注意,表示所findstringlength的out参数的长度在第一次调用后为0,在第二次调用后为1。 软连字符被计为长度为0。

解决方法是使用text.IndexOf(text, StringComparison.OrdinalIgnoreCase); ,正如你所指出的那样。 这使QCall到InternalCompareStringOrdinalIgnoreCase() ,然后调用FindStringOrdinal() ,这两个情况下返回0。

这似乎是在.NET4中的一个错误,新的变化在.NET4 Beta1恢复到以前的版本相同的.NET 2.0 / 3.0 / 3.5

.NET 4.0 CTP中BCL的新特性 (MSDN博客)

.NET 4中的string安全性更改

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中存在回到原始行为的变化。


要解决此问题 ,请将string比较方法更改为接受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); 

从文档 (我的重点):

该方法使用当前文化执行一个词( 区分大小写且区分文化)。

IE浏览器。 一些不同的代码点将被视为平等。

如果使用一个接受StringComparison值并通过StringComparison.Ordinal来避免文化依赖的重载,会发生什么?