Подтвердить что ты не робот

Regex.IsMatch vs string.Contains

Есть ли разница в скорости/использовании памяти для этих двух эквивалентных выражений:

Regex.IsMatch(Message, "1000")

Vs

Message.Contains("1000")

Любые ситуации, когда один лучше других?

Контекст этого вопроса выглядит следующим образом: Я вносил некоторые изменения в устаревший код, содержащий выражение Regex, чтобы определить, содержится ли строка в другой строке. Будучи устаревшим кодом, я не внес никаких изменений в это и в обзоре кода кто-то предположил, что Regex.IsMatch следует заменить на string.Contains. Поэтому мне было интересно, стоит ли это делать.

4b9b3361

Ответ 1

Для простых случаев String.Contains даст вам лучшую производительность, но String.Contains не позволит вам выполнять сложное сопоставление шаблонов. Используйте String.Contains для сценариев сопоставления без шаблонов (например, в вашем примере) и используйте регулярные выражения для сценариев, в которых вам нужно выполнить более сложное сопоставление шаблонов.

Регулярное выражение имеет определенное количество служебных сообщений, связанных с ним (разбор выражений, компиляция, выполнение и т.д.), что простого метода, такого как String.Contains, просто нет, поэтому String.Contains будет превосходить регулярное выражение в примеры, подобные вашим.

Ответ 2

String.Contains медленнее, когда вы сравниваете его с скомпилированным регулярным выражением. Значительно медленнее, даже!

Вы можете проверить, работает ли этот тест:

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

Ответ 3

Чтобы определить, какая из них самая быстрая, вам нужно будет сравнить вашу собственную систему. Однако регулярные выражения сложны и, скорее всего, String.Contains() будет самым быстрым и в вашем случае также самым простым решением.

Реализация String.Contains() в конечном итоге вызовет собственный метод IndexOfString(), и реализация этого будет известна только Microsoft. Однако хорошим алгоритмом для реализации этого метода является использование алгоритма Knuth-Morris-Pratt. Сложность этого алгоритма - O (m + n), где m - длина строки, которую вы ищете, и n - длина строки, которую вы ищете, что делает ее очень эффективным алгоритмом.

Фактически, эффективность поиска с использованием регулярного выражения может быть как низкая O (n) в зависимости от реализации, поэтому в некоторых ситуациях она может быть конкурентоспособной. Только эталон сможет определить это.

Если вы действительно обеспокоены поисковой скоростью, Кристиан Шаррас и Тьерри Лекрок имеют много материала о точных алгоритмах соответствия строк в Université de Rouen.

Ответ 4

@user279470 Я искал эффективный способ подсчета слов просто для удовольствия и наткнулся на this. Я дал ему файл данных OpenOffice Thesaurus для итерации. Общее количество слов составило 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

Ответ 5

Да, для этой задачи string.Contains будет почти наверняка быстрее и использовать меньше памяти. И, конечно, нет причин использовать регулярное выражение здесь.

Ответ 6

Мои собственные контрольные метки, похоже, противоречат результатам тестирования пользователя279470.

В моем случае я хотел проверить простое Regex с некоторыми операциями OR для 4 значений по сравнению с выполнением 4 x String.Contains().

Даже с 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 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";

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