piątek, 24 czerwca 2011

Regex.IsMatch się zacina!

Stworzyłeś skomplikowany Regex i po jego odpaleniu nie widzisz końca wywołania :) Pewnie pomyślałeś jak ja na początku... Regex.IsMatch się zacina! Otóż...

Niestety jesteś w błędzie

Dlaczego niestety? Ponieważ tu zaczynają się twoje problemy, tak jak i moje, kiedy próbowałem stworzyć bardziej zaawansowanego Regexa.

Przeczytanie poniższego linka powinno wszystko wyjaśnić:
Complex regex evaluation hangsW skrócie, stosowanie zagnieżdżonych kwantyfikatorów może skutkować wykładniczym czasem wykonania metody IsMatch, nie ma na to rady, możesz spróbować przebudować Regexa tak, by nie posiadał zagnieżdżonych kwantyfikatorów albo się poddać ;)

Mój Regex

Mój przypadek wyglądał następująco - próbowałem stworzyć Regex, który pozwoliłby mi sprawdzić, czy dany String składa się jedynie z jednego lub większej ilości wieloliniowych komentarzy sql (aby z porównania dwóch sqli odrzucić nieznaczące części):

^(\s*/\*(.*\s*)*\*/)+\s*$

Dopasowanie tego Regexa "zacinało się" na przypadku, w którym były dwa komentarze, a za nimi jeden dodatkowy wyraz - ten przypadek powinien zostać odrzucony, ale po tym, jak najprostsze dopasowanie nie zostało spełnione zaczęło się dopasowywanie wszystkiego ze wszystkim (nie doczekałem się wyniku;).

Poprzedni Regex był nieprawidłowy, zatwierdzał przypadki takie jak ten:

/* comment */
not comment
/* comment */

Ostatecznie stworzyłem takie dzieło, które działa i o dziwo, nawet się nie zacina:

^(\s*/\*((?!\*/|/\*)(?s).)*\*/\s*)+$

Rozłożę je teraz na czynniki pierwsze :)
* - dowolna ilość
+ - jeden lub więcej
^ - początek Stringa
$ - koniec Stringa
\s - spacje, taby, znaki końca linii, ogólnie białe znaki
/\* i \*/ - początek i koniec wieloliniowego komentarza
Zostaje nam środek:

(?!\*/|/\*)(?s).

. - każdy znak oprócz znaków końca linii
(?s). - ten przełącznik włącza dopasowanie znaków końca linii dla kropki
?! - określa, jakie wyrażenia (tutaj początek i koniec komentarza) nie mogą wystąpić po naszym głównym dopasowaniu (tutaj jest to (?s). czyli każdy znak)
(?!\*/|/\*) - znak początku i końca komentarza nie mogą wystąpić po żadnym znaku

Mam nadzieję, że w miarę to wyjaśniłem, jeśli nie to pytać w komentarzach ;)

Regex ciągle może zrobić nam psikusa

Musimy się jakoś zabezpieczyć przed "nieskończonymi" wywołaniami metody IsMatch, ja stworzyłem wątek, który w najgorszym razie przerywam:

regexThread.Start();
int timeout = 0;
while (regexThread.IsAlive)
{
    if (timeout < 10000)
    {
        Thread.Sleep(1);
        timeout += 1;
    }
    else
    {
        try
        {
            regexThread.Abort();
        }
        catch (ThreadAbortException ex)
        {
        }
    }
}

Polecam

Do niczego bym nie doszedł, gdybym nie mógł korzystać z tak świetnych internetowych narzędzi do sprawdzania Regexów:
RegExrRegular Expression TesterW zupełności starczyłoby pierwsze narzędzie, które jest niesamowicie rozbudowane, ale niestety niezupełnie działało dla Regexów ze slashem w śródku (można ominąć ten problem używając innego znaku zamiast slasha).

No i po pierwszym tematycznym poście

Teraz już musi być tylko lepiej! :)

Brak komentarzy:

Prześlij komentarz