2013-08-25 65 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:感謝您的糾正。 –