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

Регулярные выражения в С# работают медленно

Я делал небольшую работу с регулярным выражением за прошедшую неделю и сумел добиться большого прогресса, однако я все еще довольно n00b. У меня есть регулярное выражение, написанное на С#:

string isMethodRegex = 
    @"\b(public|private|internal|protected)?\s*(static|virtual|abstract)?"+
    @"\s*(?<returnType>[a-zA-Z\<\>_1-9]*)\s(?<method>[a-zA-Z\<\>_1-9]+)\s*\"+
    @"((?<parameters>(([a-zA-Z\[\]\<\>_1-9]*\s*[a-zA-Z_1-9]*\s*)[,]?\s*)+)\)";
IsMethodRegex = new Regex(isMethodRegex);

По какой-то причине при вызове регулярного выражения IsMethodRegex.IsMatch() он зависает в течение 30 + секунд в следующей строке:

"\t * Returns collection of active STOP transactions (transaction type 30) "

Кто-нибудь, как работает внутренняя часть Regex, и почему это будет так медленно при сопоставлении этой строки, а не с другими. У меня была игра с ним, и я обнаружил, что если я выберу * и круглую скобку, тогда все будет хорошо. Возможно, регулярное выражение плохо написано?

Любая помощь будет настолько оценена.

4b9b3361

Ответ 1

РЕДАКТИРОВАТЬ. Я думаю, что проблема с производительностью заключается в том, что группа соответствия <parameters> выполнена. Я перегруппировался в соответствии с первым параметром, затем любым количеством последовательных параметров или, возможно, вообще отсутствующим. Кроме того, я изменил \s* между типом параметра и именем на \s+ (я думаю, что это отвечало за LOT возврата, потому что оно не допускает пробелов, так что object может соответствовать как obj и ect с \s* не соответствует пробелам) и, похоже, работает намного быстрее:

string isMethodRegex = 
    @"\b(public|private|internal|protected)?\s*(static|virtual|abstract)?"+
    @"\s*(?<returnType>[a-zA-Z\<\>_1-9]*)\s*(?<method>[a-zA-Z\<\>_1-9]+)\s*\"+
    @"((?<parameters>((\s*[a-zA-Z\[\]\<\>_1-9]*\s+[a-zA-Z_1-9]*\s*)"+
    @"(\s*,\s*[a-zA-Z\[\]\<\>_1-9]*\s+[a-zA-Z_1-9]*\s*)*\s*))?\)";

РЕДАКТИРОВАТЬ. Как указано в @Dan, следующее просто потому, что регулярное выражение может выйти раньше.

Это действительно очень странная ситуация, но если я удалю два необязательных совпадения в начале (для public/private/internal/protected и static/virtual/abstract), то он начинает работать почти мгновенно снова:

string isMethodRegex = 
    @"\b(public|private|internal|protected)\s*(static|virtual|abstract)"+
    @"(?<returnType>[a-zA-Z\<\>_1-9]*)\s(?<method>[a-zA-Z\<\>_1-9]+)\s*\"+
    @"((?<parameters>(([a-zA-Z\[\]\<\>_1-9]*\s*[a-zA-Z_1-9]*\s*)[,]?\s*)+)\)";
var IsMethodRegex = new Regex(isMethodRegex);

string s = "\t * Returns collection of active STOP transactions (transaction type 30) ";

Console.WriteLine(IsMethodRegex.IsMatch(s));

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

Ответ 2

Я изменил несколько совпадений 0 или более (*) с 1 или более (+), где, я думаю, это имеет смысл для вашего регулярного выражения (он более подходит для Java и С#, чем для VB.NET):

string isMethodRegex =
  @"\b(public|private|internal|protected)?\s*(static|virtual|abstract)?" +
  @"\s*(?<returnType>[a-zA-Z\<\>_1-9]+)\s+(?<method>[a-zA-Z\<\>_1-9]+)\s+\" +
  @"((?<parameters>(([a-zA-Z\[\]\<\>_1-9]+\s+[a-zA-Z_1-9]+\s*)[,]?\s*)+)\)";

Теперь быстро.

Убедитесь, что он все еще возвращает ожидаемый результат.

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

Ответ 3

Вы пытались скомпилировать свое Regex?

string pattern = @"\b[at]\w+";
RegexOptions options = RegexOptions.IgnoreCase | RegexOptions.Compiled;
string text = "The threaded application ate up the thread pool as it executed.";
MatchCollection matches;

Regex optionRegex = new Regex(pattern, options);
Console.WriteLine("Parsing '{0}' with options {1}:", text, options.ToString());
// Get matches of pattern in text
matches = optionRegex.Matches(text);
// Iterate matches
for (int ctr = 1; ctr <= matches.Count; ctr++)
   Console.WriteLine("{0}. {1}", ctr, matches[ctr-1].Value);

Затем регулярное выражение выполняется только при первом выполнении.