Kiedy NIE powinieneś używać wyrażeń regularnych? [Zamknięte]

50

Wyrażenia regularne są potężnym narzędziem w arsenale programisty, ale - zdarzają się przypadki, gdy nie są najlepszym wyborem, a nawet wręcz szkodliwe.

Prostym przykładem # 1 jest parsowanie HTML za pomocą regexp - znanej drogi do licznych błędów. Prawdopodobnie wiąże się to również z parsowaniem w ogóle.

Ale czy istnieją inne wyraźne obszary, w których nie można przejść do wyrażeń regularnych?


ps: „ Pytanie, które zadajesz, wydaje się subiektywne i prawdopodobnie zostanie zamknięte. ” - dlatego chcę podkreślić, że interesują mnie przykłady, w których wiadomo, że użycie wyrażeń regularnych powoduje problemy.

c69
źródło
9
Analiza HTML za pomocą regexp to nie tylko „znana droga do licznych błędów”. To jest właściwie niemożliwe .
Kramii Reinstate Monica
19
Jest to nie tylko niemożliwe, ale prowadzi również do szaleństwa i wiecznego potępienia
Martin Wickman,
3
@ Jörg: Regexp to tylko skrót dla wyrażeń regularnych.
Joren,
3
@ Jörg: Prawdą jest, że istnieje ogromna różnica między wyrażeniami regularnymi w matematyce a ich implementacjami w bibliotekach oprogramowania. Prawdą jest również to, że większość bibliotek wyrażeń regularnych ma rozszerzenia, które znacznie wykraczają poza akceptację zwykłych języków i że nazywanie ich wyrażeniami regularnymi nie zawsze jest tak właściwe. Zgadzam się z tobą, że istnieją dwie różne koncepcje. Ale mają to samo imię; regexp jest nadal tylko skrótem, a nie terminem samym w sobie. Wiele z tych przykładów na tej stronie korzystania z pełnego terminu dla bibliotek oprogramowania.
Joren,
2
@ Jörg - to semantyka. Chociaż dobrym pomysłem może być wywoływanie tych wzorców pod różnymi nazwami (choćby po to, aby uniknąć błędnego określenia „wyrażenia regularne dotyczą języków regularnych”), „regexp” / „wyrażenia regularne” nie jest bardzo dobrą próbą i prowadzi tylko do dodatkowe zamieszanie.
Kobi,

Odpowiedzi:

60

Nie używaj wyrażeń regularnych:

  • Kiedy są parsery.

Nie ogranicza się to do HTML . Prostego prawidłowego kodu XML nie można rozsądnie przeanalizować za pomocą wyrażenia regularnego, nawet jeśli znasz schemat i wiesz, że nigdy się nie zmieni.

Nie próbuj na przykład analizować kodu źródłowego C # . Zamiast tego parsuj, aby uzyskać znaczącą strukturę drzewa lub tokeny.

  • Mówiąc bardziej ogólnie, kiedy masz lepsze narzędzia do wykonywania swojej pracy.

Co jeśli musisz szukać litery, zarówno małej, jak i dużej? Jeśli kochasz wyrażenia regularne, będziesz ich używać. Ale czy nie jest łatwiej / szybciej / czytelniej korzystać z dwóch wyszukiwań, jeden po drugim? W większości języków są szanse, że osiągniesz lepszą wydajność i zwiększysz czytelność kodu.

Na przykład przykładowy kod w odpowiedzi Ingo jest dobrym przykładem, gdy nie wolno używać wyrażeń regularnych. Wyszukaj foo, a następnie bar.

  • Podczas analizowania ludzkiego pisania.

Dobrym przykładem jest filtr nieprzyzwoitości. Nie tylko ogólnie jest to zły pomysł , ale możesz mieć ochotę zrobić to za pomocą wyrażeń regularnych, a zrobisz to źle. Istnieje wiele sposobów, w jakie człowiek może napisać słowo, liczbę, zdanie i będzie zrozumiany przez innego człowieka, ale nie przez zwykłe wyrażenie. Dlatego zamiast chwytać się prawdziwej nieprzyzwoitości, wyrażenie regularne poświęci jej czas na zranienie innych użytkowników.

  • Podczas sprawdzania poprawności niektórych typów danych.

Na przykład nie sprawdzaj poprawności adresu e-mail za pomocą wyrażenia regularnego. W większości przypadków zrobisz to źle. W rzadkich przypadkach zrobisz to dobrze i skończysz z kodowaniem horroru o długości 6 343 znaków .

Bez odpowiednich narzędzi popełnisz błędy. I zauważysz je w ostatniej chwili, a może nigdy. Jeśli nie zależy ci na czystym kodzie, napiszesz dwadzieścia wierszy bez komentarzy, spacji i znaków nowej linii.

  • Kiedy twój kod zostanie odczytany. A potem czytaj jeszcze raz i jeszcze raz, za każdym razem przez różnych programistów.

Poważnie, jeśli wezmę twój kod i będę musiał go przejrzeć lub zmodyfikować, nie chcę spędzić tygodnia próbując zrozumieć ciąg dwudziesto-liniowy wielu symboli.

Arseni Mourzenko
źródło
9
„Poważnie, jeśli wezmę twój kod i będę musiał go przejrzeć lub zmodyfikować, nie chcę spędzić tygodnia próbując zrozumieć ciąg dwudziestu wierszy z dużą ilością symboli.” +1!
funkybro,
1
To jest znacznie lepsza odpowiedź niż jego przyrodnia siostra na przepełnieniu stosu: stackoverflow.com/questions/7553722/…
Kobi
1
Jeśli używasz Perla / PCRE (i prawdopodobnie także innych nowoczesnych smaków wyrażeń regularnych), poczytaj o podprogramach, nazwanych grupach przechwytujących i (?(DEFINE))zapewnieniach;) Możesz pisać bardzo czyste wyrażenia regularne za pomocą tych, a właściwie kiedy będziesz używać tych, napiszesz gramatyki, które są bardzo podobny do tego, co napiszesz w yacc lub podobnym;)
NikiC,
2
Używanie wyrażeń regularnych do parsowania słów z czarnej listy jest klbutycznym błędem.
Dan Ray
Na świecie nie ma powodu, aby unikać rzucania wyrażeniem regularnym w taki ciąg "<a href='foo'>stuff</a>". Współczesne wyrażenia regularne nie mają z tym problemu.
tchrist
18

Najważniejsze: kiedy przetwarzany język nie jest zwykłym językiem .

HTML nie jest zwykłym językiem, a przetwarzanie go za pomocą wyrażeń regularnych nie jest możliwe (nie tylko trudne lub droga do błędnego kodu).

Matteo
źródło
4
Źle! Jeśli używasz któregokolwiek z nowoczesnych smaków wyrażeń regularnych (Perl, PCRE, Java, .NET, ...), możesz wykonywać rekurencje i asercje, a zatem możesz analizować również dopasowane gramatyki bezkontekstowe i kontekstowe.
NikiC
9
@NikiC. Nie źle. „Nowoczesne smaki wyrażeń regularnych” nie są wyrażeniami regularnymi (których można używać do analizowania języków regularnych, stąd nazwa). Zgadzam się, że dzięki PRE możesz zrobić więcej, ale nie nazwałbym ich po prostu „wyrażeniami regularnymi” (jak w pierwotnym pytaniu).
Matteo,
1
Współczesne wyrażenia regularne wykraczają daleko poza to, czego uczyła twoja babcia, że ​​wyrażenia regularne mogą to zrobić, jeśli jej rada jest nieistotna. Nawet prymitywne wyrażenia regularne mogą obsługiwać większość małych fragmentów kodu HTML. Ten ogólny zakaz jest absurdalny i nierealny. Regeksy zostały stworzone dla tego rodzaju rzeczy. I tak, wiem o czym mówię .
tchrist
12

Podczas przepływu stosów często ludzie pytają o wyrażenia regularne, które sprawdzą, czy dany ciąg nie zawiera tego czy tamtego. To jest, IMHO, odwrócenie celu wyrażenia regularnego. Nawet jeśli istnieje rozwiązanie (wykorzystujące negatywne spojrzenie za twierdzeniami lub takimi rzeczami), często znacznie lepiej jest użyć wyrażenia regularnego dla tego, co zostało stworzone i obsłużyć ujemny przypadek za pomocą logiki programu.

Przykład:

# bad
if (/complicated regex that assures the string does NOT conatin foo|bar/) {
    # do something
}

# appropriate
if (/foo|bar/) {
    # error handling
} else {
    # do something
}
Ingo
źródło
1
+1: Kilka razy unikałem zakodowania się w kącie za pomocą wyrażeń regularnych, zatrzymując się i zadając sobie pytanie „Dobra, co konkretnie próbuję dopasować?” zamiast „Czego próbuję uniknąć?”
5

Dwa przypadki:

Gdy jest łatwiejszy sposób

  • Większość języków udostępnia prostą funkcję, taką jak INSTR, w celu ustalenia, czy jeden ciąg znaków jest podzbiorem drugiego. Jeśli to właśnie chcesz zrobić, skorzystaj z prostszej funkcji. Nie pisz własnego wyrażenia regularnego.

  • Jeśli dostępna jest biblioteka do wykonywania złożonych operacji na łańcuchach, użyj jej zamiast pisać własne wyrażenie regularne.

Gdy wyrażenia regularne nie są wystarczająco potężne

  • Jeśli potrzebujesz parsera, użyj parsera.
Kramii Przywróć Monikę
źródło
0

Wyrażenia regularne nie mogą zidentyfikować struktur rekurencyjnych . To jest podstawowe ograniczenie.

Weźmy JSON - jest to dość prosty format, ale ponieważ obiekt może zawierać inne obiekty jako wartości składowe (dowolnie głębokie), składnia jest rekurencyjna i nie może być analizowana przez wyrażenie regularne. Z drugiej strony CSV może być analizowany przez wyrażenia regularne, ponieważ nie zawiera żadnych struktur rekurencyjnych.

Krótko mówiąc, wyrażenia regularne nie pozwalają wzorowi odnosić się do siebie. Nie możesz powiedzieć: w tym momencie składni ponownie dopasuj cały wzorzec. Innymi słowy, wyrażenia regularne pasują tylko liniowo, nie zawiera stosu, który pozwoliłby mu śledzić, jak głęboko jest to zagnieżdżony wzór.

Pamiętaj, że nie ma to nic wspólnego z tym, jak skomplikowany lub skomplikowany jest format. Wyrażenia S są naprawdę bardzo proste, ale nie można ich analizować za pomocą wyrażenia regularnego. Z drugiej strony CSS2 jest dość złożonym językiem, ale nie zawiera struktur rekurencyjnych i dlatego można go parsować za pomocą wyrażenia regularnego. (Chociaż nie jest to prawdą w przypadku CSS3 ze względu na wyrażenia CSS, które mają składnię rekurencyjną).

Nie dzieje się tak dlatego, że jest brzydki, skomplikowany lub podatny na analizowanie kodu HTML przy użyciu tylko wyrażeń regularnych. Jest to po prostu niemożliwe .

Jeśli musisz przeanalizować format zawierający struktury rekurencyjne, musisz przynajmniej uzupełnić użycie wyrażeń regularnych stosem, aby śledzić poziom struktur rekurencyjnych. Tak zazwyczaj działa parser. Wyrażenia regularne służą do rozpoznawania części „liniowych”, a niestandardowy kod poza wyrażeniem regularnym służy do śledzenia zagnieżdżonych struktur.

Zwykle taka analiza jest podzielona na osobne fazy. Tokenizacja to pierwsza faza, w której wyrażenia regularne są używane do podziału danych wejściowych na sekwencję „tokenów”, takich jak słowa, znaki interpunkcyjne, nawiasy kwadratowe itp. Analiza jest kolejną fazą, w której tokeny te są przetwarzane w strukturę hierarchiczną, drzewo składniowe.

Kiedy więc usłyszysz, że HTML lub C # nie mogą być analizowane za pomocą wyrażeń regularnych, pamiętaj, że wyrażenia regularne nadal są kluczową częścią parserów. Po prostu nie można parsować takiego języka przy użyciu tylko wyrażeń regularnych i kodu pomocniczego.

JacquesB
źródło