55

我们最近将所有项目从 .NET 3.5 升级到 .NET 4。我遇到了一个关于 .NET 的相当奇怪的问题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 的索引。

更新:

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

4

3 回答 3

31

您的字符串包含两个字符:软连字符(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);

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

据我可以通过框架追溯,较旧的 .NET 版本使用InternalCall to IndexOfString()(我无法弄清楚该调用哪个 API 调用),而从 .NET 4 开始使用QCallInternalFindNLSStringEx()调用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);正如您所指出的,解决方法是使用。这会产生一个 QCall InternalCompareStringOrdinalIgnoreCase(),然后调用FindStringOrdinal(),这两种情况都返回 0 。

于 2012-07-13T09:20:05.240 回答
20

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

.NET 4.0 CTP 中 BCL 的新增功能(MSDN 博客)

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

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

此更改String.IndexOf通过更改方法的行为来影响方法的行为,默认情况下将它们更改为执行序数(逐字节)比较 an 将更改为 useCultureInfo.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 中存在对原始行为的更改。


要解决此问题,请将字符串比较方法更改为接受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); 
于 2012-07-13T09:21:09.617 回答
0

文档(我的重点):

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

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

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

于 2012-07-13T09:44:33.750 回答