C # программа зависает на минуту, когда запускается куча RegExes - PullRequest
2 голосов
/ 08 января 2012

У меня есть программа, которая запускает большое количество регулярных выражений (10+) на довольно длинном наборе текстов (5-15 текстов по 1000 слов каждое)

Каждый раз, когда это делается, я чувствую, чтоЯ где-то там забыл Thread.Sleep (5000).Регулярные выражения действительно загружают процессор или что-то в этом роде?Казалось бы, компьютер должен выполнить такую ​​задачу за миллисекунду.

Должен ли я попытаться сгруппировать все регулярные выражения в ОДНОЕ выражение монстра?Это поможет?

Спасибо

РЕДАКТИРОВАТЬ: Вот регулярное выражение, которое работает прямо сейчас:

Regex rgx = new Regex(@"((.*(\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*).*)|(.*(keyword1)).*|.*(keyword2).*|.*(keyword3).*|.*(keyword4).*|.*(keyword5).*|.*(keyword6).*|.*(keyword7).*|.*(keyword8).*|.*(count:\n[0-9]|count:\n\n[0-9]|Count:\n[0-9]|Count:\n\n[0-9]|Count:\n).*|.*(keyword10).*|.*(summary: \n|Summary:\n).*|.*(count:).*)", RegexOptions.Compiled | RegexOptions.IgnoreCase);

Regex regex = new Regex(@".*(\.com|\.biz|\.net|\.org|\.co\.uk|\.bz|\.info|\.us|\.cm|(<a href=)).*", RegexOptions.Compiled | RegexOptions.IgnoreCase);

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

Ответы [ 4 ]

2 голосов
/ 08 января 2012

Регулярные выражения не убивают процессоры, авторы регулярных выражений делают. ;)

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

И это возможно. Каждое ключевое слово находится в своей собственной ветке / альтернативе, и каждая ветвь начинается с .*, поэтому первое, что делает каждая ветвь, - это поглощает оставшуюся часть текущего абзаца (т. Е. Все до следующей новой строки). Затем он начинает возвращаться, пытаясь сопоставить ключевое слово. Если он возвращается к позиции, с которой он начал, следующая ветвь вступает во владение и делает то же самое.

Когда все ветви сообщили об отказе, двигатель регулярного выражения перемещается вперед на одну позицию и снова проходит через все ветви. Это более десятка веток, умноженное на количество символов в абзаце, умноженное на количество абзацев ... Я думаю, вы поняли. Сравните это с этим регулярным выражением:

Regex re = new Regex(@"^.*?(\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*|keyword1|keyword2|keyword3|keyword4|keyword5|keyword6|keyword7|keyword8|count:(\n\n?[0-9]?)?|keyword10|summary: \n).*$", 
    RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture);

Есть три основных изменения:

  • Я учел ведущие и конечные .*
  • Я сменил ведущий на .*?, сделав его нежадным
  • Я добавил якоря начала и конца строки (^ и $ в режиме Multiline)

Теперь он делает только одну попытку сопоставления для каждого абзаца (успешно или неудачно) и практически никогда не возвращается. Я мог бы сделать это еще эффективнее, если бы знал больше о ваших данных. Например, если каждое ключевое слово / токен / что-либо начинается с буквы, граница слова будет иметь заметный эффект (например, ^.*?\b(\w+...).

Опция ExplicitCapture заставляет все «голые» группы ((...)) действовать как группы без захвата ((?:...)), еще больше уменьшая накладные расходы без добавления беспорядка в регулярное выражение. Если вы хотите захватить токен, просто измените эту первую группу на именованную группу (например, (?<token>\w+...).

1 голос
/ 08 января 2012

Никогда не предполагайте, что и по какой причине ваше приложение работает медленно. Вместо этого всегда измерьте это.

Используйте профилировщик приличной производительности (например, ANTS Performance Profiler от Red Gate - они предлагают бесплатную 14-дневную пробную версию) и действительно видите узкие места в производительности.

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

Если профилировщик подтвердит ваше предположение относительно регулярных выражений, вы можете попытаться оптимизировать их (изменив или предварительно скомпилировав).

1 голос
/ 08 января 2012

Если шаблон не имеет дело с регистром ... не используйте опцию игнорирования регистра. Смотри

Хотите более быстрые регулярные выражения? Возможно, вам стоит подумать об этой опции IgnoreCase ...

В противном случае, как указано, но указать здесь:

  1. Поместите все операции в отдельные фоновые потоки. Не держите графический интерфейс.
  2. Изучите каждый шаблон. Во многих случаях использование * (от нуля до многих), если его заменить на + (от одного до многих), может дать движку регулярных выражений реальную подсказку и не потребовать столь большого бесплодного возврата. Смотри ( Возврат )
  3. Компиляция шаблона может занять время, используйте опцию Compile, чтобы избежать повторного его анализа ... но если кто-то использует статические методы для вызова синтаксического анализатора регулярных выражений, шаблон кэшируется автоматически.
  4. См. Regex висит с моим выражением [Дэвид Гутьеррес] для некоторых других вкусностей.
1 голос
/ 08 января 2012

Прежде всего вы можете попробовать скомпилированный вариант RegexOptions.Compiled

Вот хорошая статья о производительности регулярных выражений: производительность регулярных выражений

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

И третий. У меня были некоторые проблемы с регулярным выражением. И в этом случае я использовал метод string.contains. Например:

bool IsSomethingValid(stging source, string shouldContain, string pattern)
{
    bool res = source.Contains(shouldContain);
    if (res)
    {
         var regex = new Regex(pattern, RegexOptions.Compiled);
         res = regex.IsMatch(source);
    }
    return res;
}

Если вы дали нам примеры своего сценария, мы можем попытаться улучшить их.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...