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 liczba wię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}$/