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

Используя статический Regex.IsMatch против создания экземпляра Regex

В С#, если у вас есть код вроде:

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 не является тривиальным.

4b9b3361

Ответ 1

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

Мой первоначальный ответ, сохраненный ниже, был основан на изучении версии 1.1 платформы .NET. Это довольно позорно, так как .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 раз по набору из 10 000 строк ввода (всего в 1 миллион операций).

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

Подтверждает 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, и скомпилированная версия победит.

Ответ 2

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

Ответ 3

Этот ответ более не корректен в отношении версий .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);
}

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

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

Ответ 4

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

Эта страница на компиляции и повторном использовании объектов регулярного выражения в MSDN. Таким образом, говорится:

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

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

Ответ 5

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

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

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

Ответ 6

Я предлагаю вам читать Сообщение Джеффа о компиляции Regex.

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

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

Ответ 7

Для приложения 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);
}