2013-08-25 51 views
4

我有以下代码:为什么正则表达式上的For循环比较慢?

 string pattern = @"(?:\S+\s){1,6}\S*" + search + @"\S*(?:\s\S+){1,6}"; 
     String dbContents = row[2].ToString(); 
     var matches = Regex.Matches(dbContents, pattern, RegexOptions.IgnoreCase | RegexOptions.Compiled); 
     for (int i = 0; i < matches.Count; i++) 
     { 
      if (i == 3) 
       break; 

      Contents += String.Format("... {0} ...", matches[i].Value); 
     } 

我试图做到的是搜索词后,得到搜索词前一到六个词与词1-6。在执行代码时,性能在for循环中“matches.Count”。对于非常大的字符串,它会占用一分钟以执行。我很困惑为什么以及如何解决这个问题。

回答

10

为了找到计数,必须找到全部这些匹配以便对它们进行计数。鉴于你无论如何都会在三次之后停止,这看起来有点没有意义。

使用MatchCollection的懒惰评估与LINQ的Take方法相结合,只进行前三场比赛。通常这是一个好主意,在一个循环中使用StringBuilder,而不是字符串连接,太:

StringBuilder builder = new StringBuilder(...); 
foreach (var match in matches.Cast<Match>().Take(3)) 
{ 
    builder.AppendFormat("... {0} ...", matches[i].Value); 
} 

(该StringBuilder变化可能不会在这里做太大的差别,但它是一个很好的习惯进入的。需要Cast的方法,因为Enumerable.Take仅适用于通用IEnumerable<T>型)

+0

谢谢!深夜将我的头撞在墙上。这大大提高了性能。我得到5秒的加载时间与58.67秒相比 – Chris

3

从MSDN:

Matches方法使用懒评价填充换货政...默认为 MatchCollection对象。访问此集合的成员,如 MatchCollection.Count和MatchCollection.CopyTo将立即填充集合 。要利用懒惰的评价,你应该 使用结构遍历集合,如foreach在 C#

底线:改变你的代码,使用foreach

3

另一种方式做,这是调用Match然后NextMatch,像这样:

var match = Regex.Match(dbContents, pattern, RegexOptions.IgnoreCase | RegexOptions.Compiled); 
    for (int i = 0; i < 3 && match.Success; i++) 
    { 
     Contents += String.Format("... {0} ...", matches[i].Value); 
     match = match.NextMatch(); 
    } 
+0

我认为你的意思是'Regex.Match'而不是'Regex.Matches'在第一行? –

+0

@KingKing:感谢您的纠正。 –