【问题标题】:Search every occurence of a substring in a string C#在字符串 C# 中搜索每个出现的子字符串
【发布时间】:2014-04-30 15:37:07
【问题描述】:

我知道这里已经讨论过类似的问题和问题,但不完全是我需要的。

让我们面对一个问题,我们有一根很长的绳子,一根很长的绳子,干草堆,我们有一根针。经典问题:搜索所有出现的事件。 Regex.Matches 是一个不错的函数,它只有一个问题,在我们的例子中是一个严重的问题,它不计算重叠的字符串。

例如

string haystack = "(anything)....bbb....(anything)";  
string needle = "bb"; //must be a string not a char

现在我们看到,bb 是大海捞针中 bbb 部分的 2 倍。 (BBbbBBRegex.Matches 只找到第一个,他从第三个b 进一步搜索。并且不会找到任何其他的。

知道如何在长长的大海捞针字符串中搜索具有重叠选项的毛皮子字符串(当然必须高效)?

谢谢,如有重复请见谅。

【问题讨论】:

  • 使用 string.IndexOf(string, fromPosition)

标签: c# regex string performance


【解决方案1】:

在循环中使用 string.IndexOf

string haystack = "(anything)....bbb....(anything)";  
string needle = "bb"; 
int pos = 0;
while((pos = haystack.IndexOf(needle, pos)) != -1)
{
    Console.Write("Found at pos:" + pos.ToString());
    Console.WriteLine(" - " + haystack.Substring(pos, needle.Length));
    pos++;
}

【讨论】:

  • 嗨@Steve,谢谢你的想法,似乎效果很好,我不确定这是否是最有效的方法,但它比我的解决方案更好:)
  • 嗯,至少更简单。我已经测试了 Matt 的解决方案,它的匹配速度更快,但是当您尝试打印找到的字符串时它似乎更慢,并且您的测试字符串可能只是一个示例(我认为您并没有真正搜索 bb)。但是我认为这只是一个微优化,除非您有性能问题的证据,否则我不会在这里过于担心效率。
【解决方案2】:

为什么不只维护一个指向子字符串所在位置的指针,然后在找到匹配项时从该位置 + 1 恢复您的 Regex.Matches。

【讨论】:

    【解决方案3】:

    更简单的解决方案是像其他人建议的那样在循环中使用string.IndexOf,但是......如果你真的想使用正则表达式,你可以使用积极的前瞻来处理重叠。例如:

    string test = "(anything)....bbb....(anything)";
    Regex r = new Regex("b(?=b)");
    foreach (Match m in r.Matches(test)) 
    {
        Console.WriteLine(string.Format("match at: {0}",m.Index));
    }
    

    将匹配两个索引 1415

    这对于 2 个字符的匹配是可以管理的,但对于较长的匹配可能会变得很麻烦。

    确定这是否会比循环解决方案中的string.IndexOf 更快在实践中留给读者作为练习。

    编辑:因为好奇心战胜了我,我不得不尝试找出哪个实际上更快,这里有一些测试代码:

    http://dotnetfiddle.net/qneK5u

            string test = "(anything)....bbbbbb....(anything)";
            Regex r = new Regex("b(?=b)");
            var s = new System.Diagnostics.Stopwatch();
            s.Start();
            int i=0; 
            int l=5000;
            for (;i<l;i++) {
                var result = r.Matches(test);
            }
            Console.WriteLine(s.ElapsedTicks.ToString());
            i = 0;
            string needle = "bb"; 
            int pos = 0;
            s.Reset();
            s.Start();
            for (;i<l;i++) {
                pos = 0;
                while((pos = test.IndexOf(needle, pos)) != -1)
                {
                    pos++;
                }
            }
            Console.WriteLine(s.ElapsedTicks.ToString());
    

    正则表达式解决方案的速度明显更快,但我不确定我是否进行了公平测试。

    【讨论】:

    • 我也很好奇,因为我一直认为 RegEx 比任何字符串函数都慢,但你是对的。正则表达式在匹配值方面更快。然而,这只是问题的前半部分。现在您有了一个 Match 集合,如果您尝试打印(使用)集合中的值会发生什么。在我的测试中,这个转换再次是较慢的正则表达式。
    • 嗨@Matt,你是对的,这个(正是这个)解决方案比IndexOf快30倍,但考虑到我的针是一个变量(长度和内容),对于我的问题这个无法正常工作。不过还是谢谢你!
    • @Steve:我怀疑如果没有其他原因,Match 对象比在第二种情况下使用的int 更重。如果您只需要索引,那么循环可能是一个更好的选择(而且我倾向于认为在这里使用正则表达式有点矫枉过正)。这个test 似乎表明,如果你真的循环通过MatchCollection
    • @solotherm:我很确定你可以调整它以使用可变的“针”。您基本上只需要提前查看所有不是您匹配的第一个字符的内容。无论哪种方式,它仍然可能是矫枉过正。
    【解决方案4】:

    此函数返回起始匹配针字符串的索引列表(可能比正则表达式更快):

        public static List<int>  GetPositions(string haystack , string needle)
        {
            List<int> ret = new List<int>();
            if (string.IsNullOrEmpty(haystack) || string.IsNullOrEmpty(needle))
                return ret;
            for(int i=0;i<haystack.Length-needle.Length+1;i++)
            {
                int j = 0;
                for(;j<needle.Length && haystack[i+j]==needle[j];j++);
                if(j==needle.Length )
                    ret.Add(i);
            }
            return ret;
        }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-10-06
      • 1970-01-01
      • 1970-01-01
      • 2020-09-27
      • 2012-08-16
      • 2020-01-01
      • 1970-01-01
      相关资源
      最近更新 更多