Wyrażaj się regularnie
Istnieją wokół nas i codziennie się o nie ocieramy, choć nie zdajemy sobie z tego sprawy i ich nie zauważamy. Wyrażenia regularne – regex, wyglądające w praktyce jak zapomniany almanach hakerski, niewiele mówiący przeciętnemu użytkownikowi. Niemniej, gdy czasem zanurzymy się w odmęty enigmatycznego terminala, lub natrafimy na program, którzy radośnie szczyci się obsługą takich wyrażeń – dobrze jest znać choć podstawy tego tajnego kodu, by zrozumieć jakim może być ułatwieniem.
Definicję wyrażeń regularnych (ang. regular expressions, stąd regex) można zamknąć w paru słowach – są to wzorce i przepisy na ciągi znaków. Do czego się przydają? Do wyszukiwania za jednym zamachem plików o podobnych nazwach/rozszerzeniach, treści w tekście i innych czynności związanych z tekstowym dopasowywaniem zawartości. Brzmi to może nieco wydumanie, bo co można skomplikować w wyszukiwaniu plików? Ale wystarczy przypomnieć sobie ile razy szukaliśmy na dysku np. pliku o rozszerzeniu .jpg… A może to było .png? Kiedy w operacji chcemy uwzględnić kilka wariantów – regex pozwoli dokładniej opisać nierozumnej maszynie zakres naszych oczekiwań. Wystarczy wpleść w nasze zapytanie kilka magicznych kodów i odkryjemy drogę ku nowemu. Jednak temat jest tak rozległy, że nadmiar teorii mógłby wydać się nudny, dlatego – poniżej podstawowe podstawy komunikacji ze światem za pomocą wyrażeń regularnych.
Na początek – gdzie jest koniec
Pierwsze elementy sterujące jakie warto znać, to określenie, czy wyszukiwany przez nas ciąg znaków ma znaleźć się na początku czy na końcu wyrażenia:
- ^ (daszek) rozpoczyna się od – oznacza, że podany ciąg ma znajdować się na początku wyrażenia (np. do wzorca “^tra” będzie pasował traktor, ale już nie mantra),
- $ (dolar) kończy się na – to samo, tylko od końca (czyli, do wzorca “tra$” będzie pasowała mantra, ale już nie traktor)
Jeżeli wzorzec ujmiemy w “^wzór$“, rezultatem będzie tylko i wyłącznie słowo wzór.
Jeden czy więcej, a może wcale
Kolejno możemy określić ilościowe występowanie danych znaków:
- . (kropka) jeden dowolny znak – zastępuje w wyrażeniu dowolny znak (czyli do “traktor.” będzie pasowało traktory, traktora),
- * (gwiazdka) zero lub
nieskończona liczbawięcej wystąpień znaku poprzedzającego – zastępuje w wyrażeniu dowolną liczbę wystąpień znaku wcześniejszego – najczęściej łączone z kropką (np. “^t.*r$” będzie pasowało do traktor, towar, trener, - ? (pytajnik) zero lub jedno wystąpienie znaku poprzedzającego – warunkuje istnienie wcześniejszego znaku (czyli “^traktory?$” oznacza traktor lub traktory),
- + (plus) jedno lub więcej wystąpień znaku poprzedzającego – poprzedzający znak może wystąpić co najmniej raz, lub wiele razy (czyli “^ala+” będzie pasowało dla ala i alaa, lecz nie alaaa)
Zamknięci w nawias
Nawiasy też mają konkretne i interesujące przeznaczenie w wyrażeniach regularnych:
- [ ] jeden ze znaków ujętych w nawias – dopasuje jeden ze znaków wypisany w nawiasie (czyli “^tra[knp]” pasuje do trak, tran, trap). Mamy też możliwość definiowania zakresów znaków np. “[a-z]” oznacza zakres liter od a do z, [0-9] cyfry od zero do 9, itp. Jakby tego było mało, przy użyciu ^ (daszek) włączamy negację (czyli “[^a-z]” – wszystko, oprócz zakresu liter od a do z,
- ( ) grupowanie ciągu znaków – ujęte w nawias wyrażenie dla dalszych machinacji stanowi całość (czyli np. “^(abc)+” dopasuje się do powtórzeń ciągu abcabc),
- { } ilość powtórzeń – liczba powtórzeń znaku poprzedzającego (czyli “^ala{3}” dopasuje alaaa)
Specjalnie i niespecjalnie
Na tym nie koniec uroków zaklinania znaków i wyrażeń. Regex ułatwia elastyczne dopasowanie się do treści, oraz specjalne traktowanie swoich wyjątkowych kodów:
- \ (ukośnik) znosi znaczenie metaznaków – jeżeli zachodzi potrzeba wyszukania znaków, które są jednocześnie kodami sterującymi regex, \ zniesie ich działanie i potraktuje jako zwykły znak (czyli “^Traktor\. Jeden\.$” pasuje do Traktor. Jeden.),
- | (pionowa kreska) warunkowe lub – typowy warunek (czyli “^(aaaa|bbbb)” wyraża aaaa lub bbbb)
Grunt to podstawy
I tak wygląda wprowadzenie w tematykę wyrażeń regularnych. Dla codziennego ułatwiania sobie życia powyższe wywody w większości wystarczą, a każdy kto na poważniej zamierza związać się z wyrażeniami regularnymi powinien poszerzyć swoją wiedzę w oparciu o bardziej kompleksowe opisy regex.
Ale gdzie tego używać? Zacząć można od tekstowych poleceń grep i egrep. To podstawowe narzędzia, które umożliwiają korzystanie z wyrażeń regularnych (UWAGA – grep obsługuje ‘uproszczony’ zasób metaznaków). A po wprawieniu się w konstruowaniu reguł, nie straszny będzie nam żaden innych program.
A poniżej – kilka prostych przykładów…
Kod pocztowy:
[0-9]{2}-[0-9]{3}
Adres email:
^[_a-zA-Z0-9-]+(\.[_a-zA-Z0-9-]+)*@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]{1,})*\.([a-zA-Z]{2,}){1}$
Data urodzenia:
^[0-9]{2}\.[0-9]{2}\.[0-9]{4}$/
“brak lub nieskończona liczba wystąpień znaku poprzedzającego” – bardzo niefortunne sformułowanie. 5 wystąpień to liczba jak najbardziej skończona, a będzie pasować. Może po prostu “zero lub więcej wystąpień”?
Warto też przy znaku zapytania zaznaczyć, że ma więcej, niż jedną funkcję. Służy też do ograniczania zachłanności w PCRE (np .*?).
Ogólnie – na początek OK tutorial, ale jakby ktoś się zaczynał bawić, to warto jak najszybciej sięgnąć po dokładniejszy opis.
Słuszna uwaga, nieco się zagalopowałem – poprawione, dzięki.
Co do stopnia rozbudowania to jest to pomyślane jako szybkie wprowadzenie do szybkiego stosowania podstawowych wyrażeń regularnych. Tak jak wspominałem, bardziej kompleksowe zagłębienie się w tę tematykę to temat na osobny, o wiele bardziej rozbudowany wpis. Ale są strony, które tę tematykę mają rozbudowaną właśnie od strony fachowej, dla początkująych nieco nieczytelnej.
‘alaaa’ bedzie pasować do /ala+/