Wielu programistów zna radość wymyślania szybkiego wyrażenia regularnego, obecnie często przy pomocy jakiegoś serwisu internetowego lub bardziej tradycyjnie w trybie interaktywnym, lub może pisząc mały skrypt, który ma opracowane wyrażenie regularne, oraz zbiór przypadków testowych . W obu przypadkach proces jest iteracyjny i dość szybki: hakuj tajemniczo wyglądający ciąg, aż dopasuje i uchwyci to, czego chcesz i odrzuci to, czego nie chcesz.
W prostym przypadku wynik może być mniej więcej taki, jak wyrażenie regularne Java:
Pattern re = Pattern.compile(
"^\\s*(?:(?:([\\d]+)\\s*:\\s*)?(?:([\\d]+)\\s*:\\s*))?([\\d]+)(?:\\s*[.,]\\s*([0-9]+))?\\s*$"
);
Wielu programistów zna również potrzebę edytowania wyrażeń regularnych lub po prostu kodowania wokół wyrażeń regularnych w bazie kodu starszego typu. Przy odrobinie edycji w celu podzielenia go, powyższe wyrażenie regularne jest nadal bardzo łatwe do zrozumienia dla każdego, kto zna się na wyrażeniach regularnych, a weteran wyrażeń regularnych powinien od razu zobaczyć, co robi (odpowiedz na końcu postu, na wypadek, gdyby ktoś chciał wykonać ćwiczenie wymyślenia tego sami).
Jednak rzeczy nie muszą być znacznie bardziej skomplikowane, aby wyrażenie regularne stało się naprawdę tylko do zapisu, a nawet przy starannej dokumentacji (co wszyscy oczywiście robią dla wszystkich złożonych wyrażeń regularnych, które piszą ...), modyfikacja wyrażeń regularnych staje się trudne zadanie. Może to być bardzo niebezpieczne zadanie, jeśli wyrażenie regularne nie jest dokładnie testowane jednostkowo (ale wszyscy oczywiście mają kompleksowe testy jednostkowe dla wszystkich złożonych wyrażeń regularnych, zarówno dodatnich, jak i ujemnych ...).
Krótko mówiąc, czy istnieje rozwiązanie / alternatywa zapisu / wyrażenia dla wyrażeń regularnych bez utraty ich mocy? Jak wyglądałoby powyższe wyrażenie regularne z alternatywnym podejściem? Każdy język jest w porządku, choć najlepiej byłoby rozwiązanie w wielu językach, o ile wyrażenia regularne są w wielu językach.
A potem to, co robi wcześniejsze wyrażenie regularne: parsuj ciąg liczb w formacie 1:2:3.4
, przechwytując każdą liczbę, gdzie spacje są dozwolone i tylko 3
wymagane.
Odpowiedzi:
Wiele osób wspomniało o komponowaniu z mniejszych części, ale nikt nie podał jeszcze przykładu, więc oto mój:
Nie najbardziej czytelny, ale wydaje mi się, że jest wyraźniejszy niż oryginał.
Ponadto C # ma
@
operator, który może być dodany do łańcucha, aby wskazać, że należy go wziąć dosłownie (bez znaków specjalnych), więcnumber
byłoby@"([\d]+)";
źródło
[\\d]+
i[0-9]+
powinny być tylko\\d+
(no, niektórzy mogą znaleźć[0-9]+
bardziej czytelny). Nie zamierzam edytować pytania, ale możesz chcieć naprawić tę odpowiedź.\d
pasują do wszystkiego, co jest uważane za liczbę, nawet w innych systemach numeracji (chiński, arabski itp.), Podczas gdy[0-9]
po prostu pasują do standardowych cyfr. Jednak dokonałem standaryzacji\\d
i uwzględniłem to weoptionalDecimal
wzorze.Kluczem do udokumentowania wyrażenia regularnego jest jego udokumentowanie. Zdecydowanie zbyt często ludzie podrzucają coś, co wydaje się być hałasem na linii, i porzucają to.
W ciągu Perl
/x
operator na końcu wyrażenia regularnego tłumi spacje pozwalając na dokumencie wyrażenie regularne.Powyższe wyrażenie regularne stałoby się wówczas:
Tak, to trochę pochłania pionowe białe znaki, chociaż można je skrócić bez poświęcania zbyt dużej czytelności.
Patrząc na to wyrażenie regularne, można zobaczyć, jak to działa (i nie działa). W takim przypadku wyrażenie regularne będzie pasować do ciągu
1
.Podobne podejścia można zastosować w innym języku. Działa tam opcja python re.VERBOSE .
Perl6 (powyższy przykład dotyczył perl5) idzie dalej z koncepcją reguł, która prowadzi do jeszcze potężniejszych struktur niż PCRE (zapewnia dostęp do innych gramatyk (bezkontekstowych i wrażliwych na kontekst) niż tylko zwykłe i rozszerzone regularne).
W Javie (gdzie ten przykład czerpie), można użyć konkatenacji ciągów, aby utworzyć wyrażenie regularne.
Wprawdzie tworzy to znacznie więcej
"
w ciągu, co może prowadzić do pewnych nieporozumień, może być łatwiej odczytane (szczególnie z podświetleniem składni w większości IDE) i udokumentowane.Kluczem jest rozpoznanie siły i „jednokrotnego” napisania natury, w którą często wpadają wyrażenia regularne. Kluczem jest napisanie kodu, aby obronnie tego uniknąć, aby wyrażenie regularne pozostało jasne i zrozumiałe. Formatujemy kod Java dla zachowania przejrzystości - wyrażenia regularne nie różnią się, gdy język daje taką możliwość.
źródło
Tryb „pełny” oferowany przez niektóre języki i biblioteki jest jedną z odpowiedzi na te obawy. W tym trybie białe znaki w wyrażeniu regularnym są usuwane (więc musisz użyć
\s
) i komentarze są możliwe. Oto krótki przykład w Pythonie, który domyślnie obsługuje to:W każdym języku, który tego nie robi, wdrożenie tłumacza z trybu pełnego do „normalnego” powinno być prostym zadaniem. Jeśli martwisz się o czytelność wyrażeń regularnych, prawdopodobnie z łatwością uzasadnisz tę inwestycję czasową.
źródło
Każdy język, w którym używane są wyrażenia regularne, pozwala tworzyć je z prostszych bloków, aby ułatwić czytanie, a przy czymkolwiek bardziej skomplikowanym niż (lub tak skomplikowanym) jak twój przykład, zdecydowanie powinieneś skorzystać z tej opcji. Szczególny problem z Javą i wieloma innymi językami polega na tym, że nie traktują one wyrażeń regularnych jako „obywateli pierwszej klasy”, zamiast tego wymagają od nich wkradania się do języka za pomocą literałów łańcuchowych. Oznacza to wiele znaków cudzysłowu i ukośników odwrotnych, które w rzeczywistości nie są częścią składni wyrażeń regularnych i utrudniają czytanie, a także oznacza, że nie można uzyskać dużo bardziej czytelnego bez skutecznego zdefiniowania własnego mini-języka i tłumacza.
Prototypowym lepszym sposobem integracji wyrażeń regularnych był oczywiście Perl z opcją białych znaków i operatorami wyrażeń regularnych. Perl 6 rozszerza koncepcję tworzenia wyrażeń regularnych z części na rzeczywiste gramatyki rekurencyjne, co jest o wiele lepsze w użyciu, ponieważ tak naprawdę nie ma żadnego porównania. Język mógł nie docenić łódki aktualności, ale jego wsparcie wyrażenia regularnego to The Good Stuff (tm).
źródło
Lubię używać Expresso: http://www.ultrapico.com/Expresso.htm
Ta bezpłatna aplikacja ma następujące funkcje, które z czasem wydają mi się przydatne:
Na przykład po przedłożeniu wyrażenia regularnego wyglądałoby to tak:
Oczywiście, wypróbowanie go jest warte tysiąca słów, które go opisują. Pamiętaj również, że jestem w jakikolwiek sposób powiązany z edytorem tej aplikacji.
źródło
W przypadku niektórych rzeczy pomocne może być użycie gramatyki takiej jak BNF. Mogą być one znacznie łatwiejsze do odczytania niż wyrażenia regularne. Narzędzie takie jak GoldParser Builder może następnie przekształcić gramatykę w analizator składni, który wykonuje dla ciebie ciężkie podnoszenie.
Gramatyki BNF, EBNF itp. Mogą być znacznie łatwiejsze do odczytania i wykonania niż skomplikowane wyrażenie regularne. ZŁOTO to jedno narzędzie do takich rzeczy.
W linku wiki c2 poniżej znajduje się lista możliwych alternatyw, które można przejrzeć w Google, z uwzględnieniem niektórych dyskusji na ich temat. Jest to w zasadzie link „patrz także”, aby uzupełnić moją rekomendację dotyczącą silnika gramatycznego:
Alternatywy dla wyrażeń regularnych
źródło
To stare pytanie i nie widziałem żadnej wzmianki o wyrażeniach werbalnych, więc pomyślałem, że dodam tę informację również dla przyszłych poszukiwaczy. Wyrażenia słowne zostały zaprojektowane tak, aby wyrażenia regularne były zrozumiałe dla człowieka, bez konieczności uczenia się znaczenia wyrażeń regularnych. Zobacz następujący przykład. Myślę, że to najlepiej spełnia twoje oczekiwania.
Ten przykład dotyczy javascript, teraz możesz znaleźć tę bibliotekę dla wielu języków programowania.
źródło
Najprostszym sposobem byłoby nadal użycie wyrażenia regularnego, ale zbudowanie wyrażenia z komponowania prostszych wyrażeń o nazwach opisowych, np. Http://www.martinfowler.com/bliki/ComposedRegex.html (i tak, to z konkatku ciągów)
jednak alternatywnie można również użyć biblioteki kombinatora parserów, np. http://jparsec.codehaus.org/, która da ci pełny rekurencyjny porządny parser. znowu prawdziwa moc pochodzi z kompozycji (tym razem kompozycji funkcjonalnej).
źródło
Pomyślałem, że warto wspomnieć logstash za Grok wyrażeń. Grok opiera się na idei komponowania długich parsowania wyrażeń z krótszych. Umożliwia wygodne testowanie tych elementów składowych i jest dostarczany w paczce z ponad 100 często używanymi wzorami . Poza tymi wzorami pozwala na stosowanie wszystkich składni wyrażeń regularnych.
Powyższy wzór wyrażony w grok to (przetestowałem w aplikacji do debugowania, ale mogłem się pomylić):
Opcjonalne części i spacje sprawiają, że wydaje się to trochę brzydsze niż zwykle, ale zarówno tutaj, jak iw innych przypadkach, użycie grok może uczynić twoje życie o wiele przyjemniejszym.
źródło
W F # masz moduł FsVerbalExpressions . Pozwala komponować Regeksy z wyrażeń werbalnych, ma też kilka gotowych wyrażeń regularnych (takich jak URL).
Jednym z przykładów tej składni jest:
Jeśli nie znasz składni F #, groupName jest łańcuchem „GroupNumber”.
Następnie tworzą wyrażenie słowne (VerbEx), które konstruują jako „COD (? <GroupNumber> [0-9] {3}) END”. Które następnie testują na łańcuchu „COD123END”, skąd otrzymują nazwaną grupę przechwytywania „GroupNumber”. Daje to 123.
Naprawdę uważam, że normalne wyrażenie regularne jest znacznie łatwiejsze do zrozumienia.
źródło
Po pierwsze, zrozum, że kod, który po prostu działa, jest złym kodem. Dobry kod musi również dokładnie zgłaszać wszelkie napotkane błędy.
Na przykład, jeśli piszesz funkcję przesyłania gotówki z konta jednego użytkownika na konto innego użytkownika; nie zwróciłbyś po prostu logicznego „działającego lub nieudanego”, ponieważ to nie daje dzwoniącemu żadnego pojęcia o tym, co poszło źle i nie pozwala dzwoniącemu na prawidłowe poinformowanie użytkownika. Zamiast tego możesz mieć zestaw kodów błędów (lub zestaw wyjątków): nie można znaleźć konta docelowego, niewystarczające środki na koncie źródłowym, odmowa dostępu, nie można połączyć się z bazą danych, zbyt duże obciążenie (spróbuj ponownie później) itp. .
Pomyśl teraz o przykładzie „parsuj ciąg liczb w formacie 1: 2: 3.4”. Wyrażenie regularne to tylko raport „pozytywny / negatywny”, który nie pozwala użytkownikowi na przedstawienie odpowiedniej informacji zwrotnej (niezależnie od tego, czy ta informacja zwrotna jest komunikatem o błędzie w dzienniku, czy interaktywnym interfejsem GUI, w którym błędy są wyświetlane na czerwono jako typy użytkowników lub cokolwiek innego). Jakie rodzaje błędów nie można poprawnie opisać? Zły znak w pierwszej liczbie, pierwsza liczba za duża, brak dwukropka po pierwszej liczbie itp.
Aby przekonwertować „zły kod, który po prostu działa” na „dobry kod, który zapewnia odpowiednio błędy opisowe”, musisz podzielić regex na wiele mniejszych wyrażeń regularnych (zazwyczaj wyrażenia regularne, które są tak małe, że łatwiej jest to zrobić bez wyrażeń regularnych ).
Uczynienie kodu możliwym do odczytania / utrzymania jest tylko przypadkową konsekwencją poprawienia kodu.
źródło
:
? Wyobraź sobie kompilator, który miał tylko jeden komunikat o błędzie („BŁĄD”), który był zbyt głupi, aby powiedzieć użytkownikowi, na czym polega problem. Teraz wyobraź sobie tysiące stron internetowych, które są tak samo głupie i wyświetlają (np.) „Zły adres e-mail” i nic więcej.