2010-06-03 58 views
33

是否有這兩種等價的表達速度/內存使用任何區別:Regex.IsMatch VS string.Contains

Regex.IsMatch(Message, "1000") 

Vs的

Message.Contains("1000") 

任何情況下一個是比其他更好嗎?

此問題的上下文如下所示: 我對包含Regex表達式的遺留代碼進行了一些更改,以查找某個字符串是否包含在另一個字符串中。作爲遺留代碼,我沒有做任何修改,在代碼審查中有人建議Regex.IsMatch應該被string.Contains替換。所以我想知道這個改變是否值得。

+0

[regex VS Contains。最佳性能?](http://stackoverflow.com/questions/2023792/regex-vs-contains-best-performance) – kd7 2010-06-03 00:53:32

+3

@Random,這是相關的,但一個更復雜的例子。它也使用Java,它具有不同的正則表達式語法。 – 2010-06-03 00:55:36

回答

35

對於簡單情況String.Contains會給你更好的性能,但String.Contains不會讓你做複雜的模式匹配。對於非模式匹配場景使用String.Contains(如您示例中的場景),並對需要執行更復雜模式匹配的場景使用正則表達式。

正則表達式具有與之(表達解析,編譯,執行等)等String.Contains一種簡單的方法根本沒有這就是爲什麼String.Contains將在像您的例子優於正則表達式相關聯的開銷的一定量的。

+0

顯然這是一個非常古老的答案,但也許你可以給你的消息來源? @ user279470似乎提供了一些很好的證據表明這個答案是錯誤的。 – JDB 2012-10-02 13:42:35

+1

,然後有這個基準與安德魯斯同意http://stackoverflow.com/a/17579471/289992採取您的選擇! – bottlenecked 2014-02-12 16:32:17

0

是的,對於這個任務,string.Contains幾乎肯定會更快,並且使用更少的內存。當然,這裏沒有理由使用正則表達式。

6

要確定哪個是最快的,您將不得不爲基準測試自己的系統。然而,正則表達式很複雜,而且很有可能是最快的,在你的情況下也是最簡單的解決方案。

String.Contains()的實現將最終調用本機方法IndexOfString(),並且僅由Microsoft知道該方法的實現。然而,實現這種方法的一個好的算法是使用所謂的Knuth–Morris–Pratt algorithm。該算法的複雜度爲O(m + n)其中m是您正在搜索的字符串的長度和n是您正在搜索的字符串的長度,使其成爲非常有效的算法。

實際上,使用正則表達式的搜索效率可能會很低,具體取決於具體實現,所以它在某些情況下可能仍然具有競爭性。只有一個基準將能夠確定這一點。

如果你真的關心搜索速度Christian Charras和Thierry Lecroq在魯昂大學有關於exact string matching algorithms的大量資料。

29

String.Contains當您將其與compiled regular expression進行比較時,速度會變慢。相當慢,甚至!

您可以測試它在運行這個測試:

class Program 
{ 
    public static int FoundString; 
    public static int FoundRegex; 

    static void DoLoop(bool show) 
    { 
    const string path = "C:\\file.txt"; 
    const int iterations = 1000000; 
    var content = File.ReadAllText(path); 

    const string searchString = "this exists in file"; 
    var searchRegex = new Regex("this exists in file"); 

    var containsTimer = Stopwatch.StartNew(); 
    for (var i = 0; i < iterations; i++) 
    { 
     if (content.Contains(searchString)) 
     { 
     FoundString++; 
     } 
    } 
    containsTimer.Stop(); 

    var regexTimer = Stopwatch.StartNew(); 
    for (var i = 0; i < iterations; i++) 
    { 
     if (searchRegex.IsMatch(content)) 
     { 
     FoundRegex++; 
     } 
    } 
    regexTimer.Stop(); 

    if (!show) return; 

    Console.WriteLine("FoundString: {0}", FoundString); 
    Console.WriteLine("FoundRegex: {0}", FoundRegex); 
    Console.WriteLine("containsTimer: {0}", containsTimer.ElapsedMilliseconds); 
    Console.WriteLine("regexTimer: {0}", regexTimer.ElapsedMilliseconds); 

    Console.ReadLine(); 
    } 

    static void Main(string[] args) 
    { 
    DoLoop(false); 
    DoLoop(true); 
    return; 
    } 
} 
+2

運行它在一個60kb的隨機EDIFACT INVRP文件中,「存在於文件中」填充到中途: containsTimer:84925 regexTimer:10633 – user279470 2010-09-01 10:23:26

+2

儘管它不是String.Contains(),我只是修改了一個搜索和替換函數在我的程序中使用編譯的正則表達式對象而不是'Value.ToString.IndexOf(SearchString,StringComparison.CurrentCultureIgnoreCase)'。我使用> 44,000行DataGridView(1,921替換)的replace-all測試從約7.5分鐘變爲約30秒。 – Ski 2011-10-13 15:16:44

+0

如果爲此添加.IndexOf,則它比.Contains更慢。正則表達式真棒。 – 2013-08-27 18:16:55

5

@ user279470我一直在尋找一種有效的方法來計算只是爲了好玩的單詞和跨this來了。我給了OpenOffice Thesaurus dat文件來遍歷。總字數達到1575423.

現在,我的最終目標沒有用於包含,但有趣的是看到可以調用正則表達式的不同方式使它更快。我創建了一些其他方法來將Regex的實例用法和靜態用法與RegexOptions.compiled進行比較。

public static class WordCount 
{ 
    /// <summary> 
    /// Count words with instaniated Regex. 
    /// </summary> 
    public static int CountWords4(string s) 
    { 
     Regex r = new Regex(@"[\S]+"); 
     MatchCollection collection = r.Matches(s); 
     return collection.Count; 
    } 
    /// <summary> 
    /// Count words with static compiled Regex. 
    /// </summary> 
    public static int CountWords1(string s) 
    { 
     MatchCollection collection = Regex.Matches(s, @"[\S]+", RegexOptions.Compiled); 
     return collection.Count; 
    } 
    /// <summary> 
    /// Count words with static Regex. 
    /// </summary> 
    public static int CountWords3(string s) 
    { 
     MatchCollection collection = Regex.Matches(s, @"[\S]+"); 
     return collection.Count; 
    } 

    /// <summary> 
    /// Count word with loop and character tests. 
    /// </summary> 
    public static int CountWords2(string s) 
    { 
     int c = 0; 
     for (int i = 1; i < s.Length; i++) 
     { 
      if (char.IsWhiteSpace(s[i - 1]) == true) 
      { 
       if (char.IsLetterOrDigit(s[i]) == true || 
        char.IsPunctuation(s[i])) 
       { 
        c++; 
       } 
      } 
     } 
     if (s.Length > 2) 
     { 
      c++; 
     } 
     return c; 
    } 
} 
  • regExCompileTimer.ElapsedMilliseconds 11787
  • regExStaticTimer.ElapsedMilliseconds 12300
  • regExInstanceTimer.ElapsedMilliseconds 13925
  • ContainsTimer.ElapsedMilliseconds 1074
0

我自己的板凳標記出現矛盾user279470的基準測試結果。

在我的使用案例中,我想用一些OR運算符來檢查一個簡單的Regex的4個值,而不是4 x String.Contains()。我發現String.Contains()的速度快了5倍。

using System; 
using System.Collections.Generic; 
using System.Diagnostics; 
using System.Linq; 
using Microsoft.VisualStudio.TestTools.UnitTesting; 
using System.Text.RegularExpressions; 

namespace App.Tests.Performance 
{ 
    [TestClass] 
    public class PerformanceTesting 
    { 
     private static Random random = new Random(); 

     [TestMethod] 
     public void RegexVsMultipleContains() 
     { 
      var matchRegex = new Regex("INFO|WARN|ERROR|FATAL"); 

      var testStrings = new List<string>(); 

      int iterator = 1000000/4; // div 4 for each of log levels checked 

      for (int i = 0; i < iterator; i++) 
      { 
       for (int j = 0; j < 4; j++) 
       { 
        var simulatedTestString = RandomString(50); 

        if (j == 0) 
        { 
         simulatedTestString += "INFO"; 
        } 
        else if (j == 1) 
        { 
         simulatedTestString += "WARN"; 
        } 
        else if (j == 2) 
        { 
         simulatedTestString += "ERROR"; 
        } 
        else if (j == 3) 
        { 
         simulatedTestString += "FATAL"; 
        } 

        simulatedTestString += RandomString(50); 

        testStrings.Add(simulatedTestString); 
       } 
      } 

      int cnt; 
      Stopwatch sw; 

      ////////////////////////////////////////// 
      // Multiple contains test 
      ////////////////////////////////////////// 

      cnt = 0; 
      sw = new Stopwatch(); 

      sw.Start(); 

      for (int i = 0; i < testStrings.Count; i++) 
      { 
       bool isMatch = testStrings[i].Contains("INFO") || testStrings[i].Contains("WARN") || testStrings[i].Contains("ERROR") || testStrings[i].Contains("FATAL"); 

       if (isMatch) 
       { 
        cnt += 1; 
       } 
      } 

      sw.Stop(); 

      Console.WriteLine("MULTIPLE CONTAINS: " + cnt + " " + sw.ElapsedMilliseconds); 

      ////////////////////////////////////////// 
      // Multiple contains using list test 
      ////////////////////////////////////////// 

      cnt = 0; 
      sw = new Stopwatch(); 

      sw.Start(); 

      var searchStringList = new List<string> { "INFO", "WARN", "ERROR", "FATAL" }; 

      for (int i = 0; i < testStrings.Count; i++) 
      { 
       bool isMatch = searchStringList.Any(x => testStrings[i].Contains(x)); 

       if (isMatch) 
       { 
        cnt += 1; 
       } 
      } 

      sw.Stop(); 

      Console.WriteLine("MULTIPLE CONTAINS USING LIST: " + cnt + " " + sw.ElapsedMilliseconds); 

      ////////////////////////////////////////// 
      // Regex test 
      ////////////////////////////////////////// 

      cnt = 0; 
      sw = new Stopwatch(); 

      sw.Start(); 

      for (int i = 0; i < testStrings.Count; i++) 
      { 
       bool isMatch = matchRegex.IsMatch(testStrings[i]); 

       if (isMatch) 
       { 
        cnt += 1; 
       } 
      } 

      sw.Stop(); 

      Console.WriteLine("REGEX: " + cnt + " " + sw.ElapsedMilliseconds); 
     } 

     public static string RandomString(int length) 
     { 
      const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; 

      return new string(Enumerable.Repeat(chars, length).Select(s => s[random.Next(s.Length)]).ToArray()); 
     } 
    } 
}