奇怪的string.IndexOf行为

7
我写了下面的代码片段来去除文本块中多余的空格。
int index = text.IndexOf("  ");
while (index > 0)
{
    text = text.Replace("  ", " ");
    index = text.IndexOf("  ");
}

一般来说,这个方法可以正常工作,但可能有些原始和低效。

问题

当文本中包含“ - ”时,indexOf 函数会返回匹配结果,这是非常奇怪的!Replace 函数没有删除任何内容,然后就陷入了无限循环。

有什么想法关于字符串的 indexOf 函数出了问题吗?


正则表达式替代函数 http://nlakkakula.wordpress.com/2008/09/16/removing-additional-white-spaces-in-sentence-c/ - Andrew Harry
我尝试了一下,似乎可以工作,包括2个和3个空格以及连字符。这是我的字符串:string text = "A B C - More Stuff - , hey look Working"。你能发一下你的字符串吗? - Divi
在此处发布字符串可能无法正常工作,因为SO将使用更常见的字符替换有问题的字符。请尝试以下操作。打开CharMap,找到软连字符(它位于注册商标符号R-in-circle旁边),复制,然后将其粘贴到您的代码中,然后尝试运行。 - Lasse V. Karlsen
1个回答

22

啊,文本的乐趣。

你在SO上发布时可能遗失了其中的内容,但你最有可能看到的是“软连字符”。

为了再现这个问题,我在 LINQPad 中尝试了以下代码:

void Main()
{
    var text = "Test1 \u00ad Test2";
    int index = text.IndexOf("  ");
    while (index > 0)
    {
        text = text.Replace("  ", " ");
        index = text.IndexOf("  ");
    }
}

很明显,上面的代码只是陷入了一个循环。

注意,\u00ad 是软连字符的Unicode符号,根据CharMap。你始终可以从CharMap复制并粘贴该字符,但在SO上发布它将被其更常见的近亲连字号取代,Unicode符号为 u002d (键盘上的那个)。

您可以在String Class的文档中阅读一个小节,其中有关于此主题的说明:

字符串搜索方法,例如String.StartsWith和String.IndexOf,也可以执行区分语言或基数字符串比较。以下示例说明了使用IndexOf方法进行基数和区分语言比较之间的差异。当前区域设置为英语(美国)的区分语言搜索认为子字符串“oe”匹配连字“œ”。由于软连字符(U+00AD)是零宽字符,所以搜索将软连字符视为等同于Empty,并在字符串开头找到匹配项。另一方面,基数搜索在任何情况下都不会发现匹配项。

我已经突出显示了相关部分,但我还记得有一篇关于这个确切问题的博客文章,但今晚我的Google-Fu失败了。

问题在于IndexOf和Replace使用不同的方法定位文本。

而IndexOf将软连字符视为“实际不存在”,因此发现其两侧的两个空格为“两个连接的空格”,而Replace方法则不会,因此不会删除它们中的任何一个。因此,存在循环继续迭代的条件,但由于Replace没有删除符合条件的空格,因此它永远不会结束。毫无疑问,在Unicode符号空间中还有其他类似问题的字符,但这是我见过的最典型的情况。

至少有两种处理方法:

  1. 您可以使用Regex.Replace,它似乎没有这个问题:

    text = Regex.Replace(text, "  +", " ");
    

    就我个人而言,我可能会在正则表达式中使用空格特殊字符,即\s,但如果你只想匹配空格,上面的代码也能实现。

  2. 你可以显式地要求IndexOf使用基于字典序的比较方式,这样就不会被文本所干扰了……好吧……这是一种方法:

  3. index = text.IndexOf("  ", StringComparison.Ordinal);
    

哇!今天我学到了一些东西。还有其他情况下,IndexOf的行为与顺序比较不同吗? - Jimmy
无论在哪里您都可以指定一个CultureInfo对象,观察使用不带该参数的重载时的默认值总是很有价值的。而文本正是其中之一,通常通过工作与否来衡量回报。 - Lasse V. Karlsen
软连字符?哇,这真是一个坑点 - 感谢您提供的出色答案! - Andrew Harry
请注意,软连字符在源代码中通常是可见的,但在显示文本中通常不应出现。它的目的是标记可以拆分单词的位置。您可以在允许断开单词的点内部添加它到长单词中,因为知道显示该单词不会带来大量的小“减号”。 - Lasse V. Karlsen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接