Regex.IsMatch vs string.Contains

¿Hay alguna diferencia en el uso de velocidad / memoria para estas dos expresiones equivalentes?

Regex.IsMatch(Message, "1000") 

Vs

 Message.Contains("1000") 

¿Alguna situación donde uno es mejor que otro?

El contexto de esta pregunta es el siguiente: estaba realizando algunos cambios en el código heredado que contenía la expresión Regex para encontrar si una cadena está contenida dentro de 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.

Para casos simples, String.Contains le proporcionará un mejor rendimiento, pero String.Contains no le permitirá hacer una coincidencia compleja de patrones. Use String.Contains para String.Contains 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 a ella (análisis de expresión, comstackció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. .

String.Contains es más lento cuando lo comparas con una expresión regular comstackda . ¡Mucho más lento, incluso!

Puedes probarlo ejecutando este benchmark:

 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; } } 

Para determinar cuál es el más rápido, deberá 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 el algoritmo Knuth-Morris-Pratt . 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 eficiencia de la búsqueda mediante la expresión regular puede ser tan baja como O (n) dependiendo de la implementación, por lo que puede ser competitiva 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 mucho material sobre los algoritmos de coincidencia exacta de cadenas en la Universidad de Rouen.

@ user279470 Estaba buscando una manera eficiente de contar palabras solo por diversión y encontré esto . Le di el archivo del dato de OpenOffice Thesaurus para iterar. Total Word Count llegó a 1575423.

Ahora, mi objective final no tenía ningún uso para contener, pero lo interesante era ver las diferentes formas 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 { ///  /// Count words with instaniated Regex. ///  public static int CountWords4(string s) { Regex r = new Regex(@"[\S]+"); MatchCollection collection = r.Matches(s); return collection.Count; } ///  /// Count words with static compiled Regex. ///  public static int CountWords1(string s) { MatchCollection collection = Regex.Matches(s, @"[\S]+", RegexOptions.Compiled); return collection.Count; } ///  /// Count words with static Regex. ///  public static int CountWords3(string s) { MatchCollection collection = Regex.Matches(s, @"[\S]+"); return collection.Count; } ///  /// Count word with loop and character tests. ///  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

Mis propios puntos de referencia parecen contradecir los resultados del índice de referencia de user279470.

En mi caso de uso, quería verificar un Regex simple con algunos operadores OR para 4 valores en lugar de 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(); 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 { "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 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; return new string(Enumerable.Repeat(chars, length).Select(s => s[random.Next(s.Length)]).ToArray()); } } } 

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í.