我们最近将所有项目从.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的索引。
更新
将原始问题字符串更改为每个实际字符清晰可见的字符串(使用转义)。这有点简化了问题。
答案 0 :(得分:32)
您的字符串包含两个字符: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版本使用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.注意length
,一个表示找到的字符串长度的out参数,在第一次调用后为0,在第二次调用后为1;软连字符被计为长度为0。
解决方法是使用text.IndexOf(text, StringComparison.OrdinalIgnoreCase);
,正如您所指出的那样。这使QCall成为InternalCompareStringOrdinalIgnoreCase()
,而{{1}}又调用FindStringOrdinal()
,两种情况都返回0。
答案 1 :(得分:20)
这似乎是 .NET4 中的一个错误,并且 .NET4 Beta1 中的新更改已恢复为与 .NET 2.0 / 3.0 / 3.5相同的先前版本< / em>的
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
枚举作为参数的重载,并指定Ordinal
或OrdinalIgnoreCase
。
// 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);
答案 2 :(得分:0)
从documentation(我的重点):
此方法使用当前文化执行单词( 区分大小写且对文化敏感的 )搜索。
IE中。一些不同的代码点将被视为相等。
如果您使用带有StringComparison
值并传递StringComparison.Ordinal
的重载以避免文化依赖性,会发生什么?