2011-10-04 18 views
12

Ho fatto un po 'di lavoro con regex durante la scorsa settimana e sono riuscito a fare molti progressi, tuttavia, sono ancora abbastanza n00b. Ho una regex scritto in C#:Espressioni regolari in C# che funzionano lentamente

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

Per qualche ragione, quando si chiama l'IsMethodRegex.IsMatch espressione regolare() si blocca per 30 + secondi sul seguente stringa:

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

C'è qualcuno come le parti interne di Regex funzionano e perché questo sarebbe così lento nell'accoppiare questa stringa e non con altre. Ho avuto un gioco con esso e ho scoperto che se ho eliminato * e la parentesi, allora funziona bene. Forse l'espressione regolare è scritta male?

Qualsiasi aiuto sarebbe molto apprezzato.

+2

Hai provato a compilare la regex? Uno dei sovraccarichi del costruttore fornisce questa capacità. –

+0

@Steve: mi sono appena messo alla prova e dopo averlo compilato ci vuole ancora molto tempo. Posso riprodurre il lungo tempo sulla mia macchina in LinqPad. – mellamokb

+2

So che questo è oltre il punto, ma la tua regex mi sembra molto fragile. In qualche modo penso che le prestazioni siano l'ultimo dei tuoi problemi. – ChaosPandion

risposta

3

EDIT: Penso che il problema di prestazioni sia dovuto al modo in cui il gruppo di corrispondenza <parameters> ha terminato. Ho riorganizzato per abbinare un primo parametro, quindi un numero qualsiasi di parametri successivi, o facoltativamente nessuno. Inoltre ho cambiato il \s* tra il tipo di parametro e il nome in \s+ (penso che questo sia stato il motivo di un sacco di backtracking perché non consente nessuno spazio, in modo che object possa corrispondere come obj e ect con \s* corrispondente a nessuno spazio) e sembra funzionare molto più veloce:

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*))?\)"; 

EDIT: Come opportunamente sottolineato da @ Dan, il seguente è semplicemente perché il Regex può uscire in anticipo.

questa è davvero una situazione molto strana, ma se mi tolgo la due abbinamento opzionale all'inizio (per pubblico/privato/interno/protetto e statico/virtuale/abstract) allora comincia a funzionare quasi istantaneamente ancora:

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

Tecnicamente è possibile suddividere in quattro Regex separati per ogni possibilità di gestire questa particolare situazione. Tuttavia, mentre tentate di affrontare scenari sempre più complicati, è probabile che vi imbattete in questo problema di prestazioni ancora e ancora, quindi questo probabilmente non è l'approccio ideale.

+0

Il risultato di questo è un 'falso', corretto? Può escludere immediatamente la partita perché non inizia con 'p' o 'i', quindi è una ricerca banale. –

+0

@Dan: In effetti tu hai ragione. Sospetto che abbia qualcosa a che fare con il modo in cui la parte '' è terminata. – mellamokb

3

Ho modificato alcune corrispondenze 0 o più (*) con 1 o più (+), dove I penso ha senso per la tua espressione regolare (è più adatto a Java e C# rispetto a 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*)+)\)"; 

È veloce ora.

Si prega di verificare se restituisce ancora il risultato previsto.

Per alcuni sottofondi di espressioni regolari errate, consultare here.

1

Hai provato a compilare il tuo 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); 

Quindi l'espressione regolare è lenta solo alla prima esecuzione.

+0

Abbiamo già provato, non ha fatto differenza. In effetti c'era una risposta che diceva la stessa cosa da un po 'di tempo che l'autore cancellava. – mellamokb