Wyrażenia regularne: zachłanna kropka, brak kotwic i zagnieżdżone powtórzenia

Spis treści
Wyrażenie regularne sprawdza się na przypadku, dla którego powstało, trafia i idzie do użytku. Co robi z drugim przypadkiem, to osobne pytanie, a odpowiedź brzmi często: trafia i jego – tyle że nie w to miejsce, o które chodziło.
Trzy poniższe błędy przechodzą każdy przegląd, bo żaden z nich nie zawodzi. Dają coś, to coś wygląda w próbce wiarygodnie, a różnica objawia się jako lekko błędne dane, a nie jako komunikat o błędzie.

Kropka jest zachłanna i kropka nie jest znakiem
.* bierze wszystko, co może dostać, i oddaje tylko to, co oddać musi. W wierszu z dwoma odnośnikami href="(.*)" zaczyna przy pierwszym href, biegnie do końca wiersza, a potem cofa się do ostatniego osiągalnego cudzysłowu – wynikiem jest trafienie zawierające oba odnośniki i znaczniki pomiędzy nimi.
Naprawiają to dwie rzeczy. Leniwy kwantyfikator .*? zatrzymuje się przy pierwszym cudzysłowie, który pozwala dopasować resztę wzorca. Klasa zaprzeczona [^"]* nie potrafi w ogóle przekroczyć cudzysłowu, mówi więc to samo bez żadnych nawrotów – i to ta wersja zasługuje na zapisanie, bo wypowiada warunek, zamiast polegać na tym, jak maszyna się cofa.
Klasa zaprzeczona obejmuje złamanie wiersza
Kropka nie trafia złamania wiersza, dopóki nie ustawi się przełącznika s. Zaprzeczona klasa znaków trafia je – [^&] znaczy wszystko, co nie jest ampersandem, a złamanie wiersza ampersandem nie jest.
tekst utm_source=newsletter
utm_medium=email
^utm_([a-z_]+)=([^&]+)$ z przełącznikiem m: 2 trafienia
^utm_([a-z_]+)=([^&]+) bez niego: 1 trafienie przez oba wiersze
^utm_([a-z_]+)=([^&\s]+) naprawa: białe znaki też wykluczone
To ten błąd, który wygląda na problem z danymi. Wzorzec wyciągający parametry z wielowierszowego wejścia zwraca wartość przypadkiem zawierającą wszystko, co po niej następuje, wartość trafia do pola, a pole wygląda na bardzo długą nazwę kampanii, a nie na usterkę.
Kotwice mówią gdzie, a nie czy
Bez kotwic wzorzec trafia gdziekolwiek w wejściu, co przy wyciąganiu zwykle jest pożądane, a przy sprawdzaniu prawie nigdy. Sprawdzenie, czy pole zawiera same cyfry, przechodzi z [0-9]+ również dla abc123def, bo o resztę wzorca nikt nie zapytał.
Sprawdzanie potrzebuje ^ i $ po obu stronach oraz wiedzy, co te dwa znaczą w używanej odmianie: bez przełącznika wielowierszowego początek i koniec całego wejścia, z nim – każdego wiersza. Które z dwóch jest właściwe, zależy wyłącznie od tego, czy wejście jest jedną wartością, czy ich listą – a to pytanie zasługuje na wyraźną odpowiedź zamiast na ustawienie domyślne, które przyniosła biblioteka.
Zagnieżdżone powtórzenie to problem innego rodzaju
Trzy powyższe błędy dają odpowiedź błędną. Ten nie daje żadnej. Gdy powtarzana grupa zawiera coś, co samo daje się powtarzać – (a+)+, (\s*\w+)*, (.*,)* – istnieje wykładniczo wiele sposobów rozdzielenia wejścia między powtórzenia, a maszyna próbuje ich wszystkich, zanim stwierdzi, że trafienia nie ma.
| Wzorzec | Wobec niepasującego wejścia długości n |
|---|---|
(a+)+$ |
wykładniczo – trzydzieści znaków to już sekundy |
(\s*\w+)*$ |
ten sam kształt, schowany za dwiema niewinnie wyglądającymi klasami |
^(\w+\s?)+$ |
rozpowszechniony wzorzec do sprawdzania imion i awaria usługi |
a+$ |
liniowo – zewnętrzne powtórzenie było całym problemem |
Niebezpieczną własnością jest to, że wzorce te są szybkie przy każdym wejściu, które pasuje. Wybuch następuje tylko przy wejściach niepasujących – wzorzec sprawdzający zachowuje się więc latami nienagannie, potem trafia na jedno wadliwe zgłoszenie i zajmuje proces roboczy, dopóki ktoś go nie ubije.
Reguła przeciw temu jest mechaniczna: żadnego kwantyfikatora bezpośrednio wokół grupy, której zawartość sama jest powtarzana. Gdzie tego nie da się uniknąć, klasa zaprzeczona zwykle wyraża ten sam zamiar – [^,]+, zamiast (.*,) – i zabiera wieloznaczność, którą nawroty przetrząsały.
Co sprawdzić, zanim zaufa się wzorcowi
Trzy wejścia odpowiadają na większość. Przypadek, dla którego wzorzec powstał, dla potwierdzenia, że trafia. Przypadek z dwoma wystąpieniami – tam ujawnia się zachłanność, a przy jednym przykładzie nie ujawni się nigdy. I przypadek, który trafić nie może; tam zdradzają się zarówno kotwice, jak i zagnieżdżone powtórzenie – jedne tym, że trafiają mimo wszystko, drugie tym, że nie kończą pracy.
Trzeci jest tym pomijanym i tym, który łapie oba problemy niewidoczne przy czytaniu wzorca. Zestaw sprawdzeń dla wyrażenia regularnego ma trzy wiersze i jest różnicą między wyrażeniem, które działa, a takim, któremu nikt jeszcze nie zaprzeczył.