2010-06-03 8 views
33

¿Hay alguna diferencia en el uso de velocidad/memoria para estas dos expresiones equivalentes:Regex.IsMatch vs string.Contains

Regex.IsMatch(Message, "1000") 

Vs

Message.Contains("1000") 

Cualquier situación donde uno es mejor que otro?

El contexto de esta pregunta es el siguiente: Estaba haciendo algunos cambios en el código heredado que contenía la expresión Regex para encontrar si una cadena está contenida en otra cadena. Siendo código heredado, no hice ningún cambio en eso y en la revisión del código alguien sugirió que Regex.IsMatch debería ser reemplazado por string.Contains. Así que me preguntaba si el cambio valía la pena.

+0

posible duplicado de [regex VS Contiene. ¿Mejor rendimiento?] (Http://stackoverflow.com/questions/2023792/regex-vs-contains-best-performance) – kd7

+3

@Random, eso está relacionado, pero es un ejemplo más complicado. También usa Java, que tiene una sintaxis de expresiones regulares diferente. –

Respuesta

35

Para casos simples String.Contains le dará un mejor rendimiento, pero String.Contains no le permitirá hacer una coincidencia compleja de patrones. Use String.Contains para escenarios que no coincidan con el patrón (como el de su ejemplo) y use expresiones regulares para escenarios en los que necesite hacer una coincidencia de patrones más compleja.

Una expresión regular tiene una cierta cantidad de sobrecarga asociada (análisis de expresión, compilación, ejecución, etc.) que un método simple como String.Contains simplemente no tiene y por eso String.Contains superará una expresión regular en ejemplos como el suyo .

+0

Obviamente esta es una respuesta muy antigua, pero ¿tal vez podría dar sus fuentes? @ user279470 parece presentar algunas pruebas muy buenas de que esta respuesta es incorrecta. – JDB

+1

y luego está este punto de referencia que concuerda con Andrew http://stackoverflow.com/a/17579471/289992 Haga su elección! – bottlenecked

0

Sí, para esta tarea, string.Contains seguramente será más rápido y usará menos memoria. Y, por supuesto, no hay ninguna razón para usar expresiones regulares aquí.

6

Para determinar cuál es el más rápido tendrá que comparar su propio sistema. Sin embargo, las expresiones regulares son complejas y es probable que String.Contains() sea el más rápido y en su caso también la solución más simple.

La implementación de String.Contains() eventualmente llamará al método nativo IndexOfString() y la implementación de eso solo es conocida por Microsoft. Sin embargo, un buen algoritmo para implementar este método es usar lo que se conoce como Knuth–Morris–Pratt algorithm. La complejidad de este algoritmo es O (m + n) donde m es la longitud de la cadena que está buscando y n es la longitud de la cadena que está buscando, lo que lo convierte en un algoritmo muy eficiente.

En realidad, la eficacia de la búsqueda mediante expresiones regulares puede ser tan baja O (n) dependiendo de la implementación, por lo que aún puede ser competitivo en algunas situaciones. Solo un punto de referencia podrá determinar esto.

Si está realmente preocupado por la velocidad de búsqueda, Christian Charras y Thierry Lecroq tienen un montón de material sobre exact string matching algorithms en la Universidad de Rouen.

29

String.Contains es más lento cuando se compara con un compiled regular expression. ¡Mucho más lento, incluso!

usted puede probar que funcione este punto de referencia:

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

ejecutarlo en un archivo INVRP EDIFACT aleatoria de 60 kb con "esto existe en el archivo de" relleno en la mitad de: containsTimer: 84925 regexTimer: 10633 – user279470

+2

Aunque no es String.Contains(), que acaba de modificar una búsqueda y reemplazar la función en mi programa para usar un objeto Regex compilado en lugar de 'Value.ToString.IndexOf (SearchString, StringComparison.CurrentCultureIgnoreCase)'. Mi prueba de reemplazo con una fila de datos de más de 44,000 filas (1.921 reemplazos) pasó de ~ 7.5 minutos a ~ 30 segundos. – Ski

+0

Si agrega .IndexOf a esto, es incluso más lento que .Contains. Regex es simplemente increíble. –

5

@ user279470 que estaba buscando una forma eficaz para contar las palabras sólo por diversión y me encontré con this. Le di el archivo del dato de OpenOffice Thesaurus para iterar.Total Word Count vino a 1575423.

Ahora, mi objetivo final no tenía un uso para el contenido, pero lo que era interesante era ver las diferentes maneras en que puede llamar a Regex para que sea aún más rápido. Creé algunos otros métodos para comparar el uso de una instancia de expresiones regulares y un uso estático con 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

mis propios puntos de referencia parecen contradecir los resultados de referencia de user279470 .

En mi caso de uso, quería comprobar un Regex simple con algunos operadores de quirófano para 4 valores en lugar de hacer 4 x String.Contains().

Incluso con 4 x String.Contains(), encontré que String.Contains() era 5 veces más rápido.

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()); 
     } 
    } 
} 
Cuestiones relacionadas