использование статического Regex.IsMatch против создания экземпляра Regex - PullRequest
41 голосов
/ 05 января 2009

В C # вы должны иметь такой код:

public static string importantRegex = "magic!";

public void F1(){
  //code
  if(Regex.IsMatch(importantRegex)){
    //codez in here.
  }
  //more code
}
public void main(){
  F1();
/*
  some stuff happens......
*/
  F1();
}

или вам следует сохранить экземпляр регулярного выражения, содержащий важный шаблон? Какова стоимость использования Regex.IsMatch? Я полагаю, что в каждом экземпляре Regex создается NFA. Из того, что я понимаю, это создание NFA нетривиально.

Ответы [ 7 ]

48 голосов
/ 05 января 2009

В редком отклонении от моего типичного эгоизма я как бы переворачиваюсь с этим ответом.

Мой оригинальный ответ, сохраненный ниже, был основан на проверке версии 1.1 .NET Framework. Это довольно постыдно, поскольку .NET 2.0 отсутствовал более трех лет на момент моего ответа и содержал изменения в классе Regex, которые значительно влияют на разницу между статическими и экземплярами методов.

В .NET 2.0 (и 4.0) статическая функция IsMatch определяется следующим образом:

public static bool IsMatch(string input, string pattern){
    return new Regex(pattern, RegexOptions.None, true).IsMatch(input);
}

Существенная разница здесь в том, что маленький true в качестве третьего аргумента. Это соответствует параметру с именем «useCache». Если это так, то проанализированное дерево извлекается из кэша при втором и последующем использовании.

Это кэширование поглощает большую часть - но не все - разницы в производительности между статическими и экземплярами методов. В моих тестах статический метод IsMatch все еще был примерно на 20% медленнее, чем метод экземпляра, но это составляло примерно половину секунды при 100-кратном запуске из набора из 10000 входных строк (всего 1 миллион операций ).

Это замедление на 20% все еще может быть значительным в некоторых сценариях. Если вы обнаружите, что перестраиваете сотни миллионов строк, вы, вероятно, захотите сделать каждый шаг, чтобы сделать его более эффективным. Но я бы поспорил, что в 99% случаев вы используете конкретное регулярное выражение не более нескольких раз, а лишняя миллисекунда, которую вы теряете статическим методом, не будет даже близка к заметной.

Реквизит devgeezer , который указал на это почти год назад, хотя, казалось, никто не заметил.

Мой старый ответ следующий:


Статическая функция IsMatch определяется следующим образом:

public static bool IsMatch(string input, string pattern){
    return new Regex(pattern).IsMatch(input);
}

И, да, инициализация Regex объекта не тривиальна. Вы должны использовать статические IsMatch (или любые другие статические Regex функции) в качестве быстрого ярлыка только для шаблонов, которые вы будете использовать только один раз. Если вы будете использовать шаблон повторно, стоит также повторно использовать объект Regex.

Относительно того, следует ли указывать RegexOptions.Compiled, как предполагает Джон Скит, это другая история. Ответ есть: это зависит. Для простых шаблонов или для шаблонов, используемых только несколько раз, может быть быстрее использовать не скомпилированный экземпляр. Вы должны определенно профиль, прежде чем принять решение. Стоимость компиляции объекта регулярного выражения действительно достаточно велика и может не стоить этого.


Возьмем, к примеру, следующее:

const int count = 10000;

string pattern = "^[a-z]+[0-9]+$";
string input   = "abc123";

Stopwatch sw = Stopwatch.StartNew();
for(int i = 0; i < count; i++)
    Regex.IsMatch(input, pattern);
Console.WriteLine("static took {0} seconds.", sw.Elapsed.TotalSeconds);

sw.Reset();
sw.Start();
Regex rx = new Regex(pattern);
for(int i = 0; i < count; i++)
    rx.IsMatch(input);
Console.WriteLine("instance took {0} seconds.", sw.Elapsed.TotalSeconds);

sw.Reset();
sw.Start();
rx = new Regex(pattern, RegexOptions.Compiled);
for(int i = 0; i < count; i++)
    rx.IsMatch(input);
Console.WriteLine("compiled took {0} seconds.", sw.Elapsed.TotalSeconds);

При count = 10000, как указано в списке, второй выход самый быстрый. Увеличьте count до 100000, и скомпилированная версия победит.

31 голосов
/ 05 января 2009

Если вы собираетесь многократно использовать регулярное выражение, я бы создал его с RegexOptions.Compiled и кэшировал его. Нет смысла заставлять фреймворк анализировать регулярные выражения каждый раз, когда вы этого захотите.

11 голосов
/ 31 мая 2012

Этот ответ больше не верен в отношении версий .NET, установленных на моей машине. 4.0.30319 и 2.0.50727 имеют следующее для IsMatch:

public static bool IsMatch(string input, string pattern)
{
  return new Regex(pattern, RegexOptions.None, true).IsMatch(input);
}

Истинное значение для параметра конструктора с именем "useCache". Все конструкторы Regex в конечном итоге соединяются через этот, статики называют его напрямую - передавая 'true'.

Вы читаете больше в блоге BCL об оптимизации производительности Regex, подчеркивая использование кэша статических методов здесь . Этот блог также цитирует измерения производительности. Чтение серии постов в блоге по оптимизации производительности Regex - отличное место для начала.

8 голосов
/ 05 января 2009

Я согласен с Джоном и просто уточнить, что это будет выглядеть примерно так:

static Regex regex = new Regex("regex", RegexOptions.Compiled);

Также стоит взглянуть на перечисление RegexOptions для других флагов, которые иногда могут быть полезны.

6 голосов
/ 02 августа 2009

Есть много вещей, которые влияют на производительность использования регулярного выражения. В конечном счете, единственный способ определить наиболее эффективный в вашей ситуации - это измерить, используя максимально реалистичную ситуацию.

Страница на компиляции и повторном использовании объектов регулярных выражений в MSDN покрывает это. В целом, это говорит

  1. Скомпилированные регулярные выражения требуют времени для компиляции, и после компиляции их память будет освобождена только при AppDomain выгрузках. Нужно ли использовать компиляцию или нет, зависит от количества используемых вами шаблонов и от того, как часто они используются.

  2. Статические Regex методы кэшируют проанализированное представление регулярного выражения для последних 15 (по умолчанию) шаблонов. Поэтому, если вы не используете много разных шаблонов в своем приложении или ваше использование достаточно кластеризовано, не будет большой разницы между вы , кэширующим экземпляр, или фреймворком, кэширующим его.

3 голосов
/ 05 января 2009

Предлагаю вам прочитать пост Джеффа о компиляции Regex.

Что касается вопроса, если вы задаете этот вопрос, это означает, что вы собираетесь использовать его только один раз. Итак, это действительно не имеет значения, так как разборка Regex в Reflector.IsMatch:

public static bool IsMatch(string input, string pattern, RegexOptions options)
{
    return new Regex(pattern, options, true).IsMatch(input);
}
2 голосов
/ 06 января 2009

Для приложения WinForm, над которым я работал, мы могли определить регулярное выражение для допустимых символов, которое будет выполняться при каждом нажатии клавиши, и проверку текста для любых текстовых полей (приложение ввода данных), поэтому я использовал кэш или скомпилированные регулярные выражения, такие как

  private static Dictionary<string, Regex> regexCache = new Dictionary<string, Regex>(20);

Где выражение регулярного выражения было ключом.

Тогда у меня была статическая функция, которую я мог вызывать при проверке данных:

public static bool RegExValidate(string text, string regex)
{
  if (!regexCache.ContainsKey(regex))
  {
    Regex compiledRegex = new Regex(regex,RegexOptions.Compiled);
    regexCache.Add(regex, compiledRegex);
  }
  return regexCache[regex].IsMatch(text);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...