pprogramowanie;

// blog o programowaniu i branży IT

rss

Budowanie wyrażeń regularnych

22 września 2013, kategoria: PHP
budowanie-wyrazen-regularnych

Wyrażenia regularne pełnią istotną rolę w PHP. Umożliwiają one opisywanie i przetwarzanie długich ciągów znaków. Dzieje się to na zasadzie porównania danego ciągu znaków z określonym wzorem, ułożonym przez programistę.

Czym są wyrażenia regularne?

Wyrażenia regularne to bardzo przydatne narzędzie istniejące nie tylko w języku PHP (na którym dziś się skupimy). Dzięki nim, możliwe jest wykonanie wielu operacji na raz np. wyszukiwanie określonych elementów, walidacja adresów email, walidacja adresów URL oraz zamiana poszczególnych fragmentów strony.

Zadaniem programisty jest zaprojektowanie określonego wzorcu (ang. pattern). Następnie odpowiednie funkcje wyrażeń regularnych próbują dopasować do niego tekst. W artykule opiszę proces projektowania wzorców, które będziesz mógł następnie użyć w odpowiednich funkcjach wyrażeń regularnych.

Operatory wyrażeń regularnych

Operator Opis Priorytet
^ Operator początku ciągu znaków. prawostronny
$ Operator końca ciągu znaków. lewostronny
. Operator jednego dowolnego znaku. n/d
[a-z] Dowolne litery od a do z. n/d
[A-Z] Dowolne litery od A do Z. n/d
[0-9] Dowolne cyfry od 0 do 9. n/d
[^ ] Operator negacji zbioru. n/d
* Operator powtórzenia 0 lub więcej razy. lewostronny
+ Operator powtórzenia 1 lub więcej razy. lewostronny
? Operator powtórzenia 1 lub 0 razy. lewostronny
| Operator alternatywy. obustronny
( ) Wyrażenie wewnątrz nawiasów jest atomem (rozpatrujemy je jako całość). n/a
{x} Operator powtórzenia dokładnie ‚x’ razy. lewostronny
{x,y} Operator powtórzenia minimum ‚x’ i maksimum ‚y’ razy. lewostronny

Zaprezentowane operatory na pierwszy rzut oka mogą wydawać się niezrozumiałe. Zanim zaczniemy budować wyrażenia regularne, musisz poznać jeszcze kilka ważnych informacji. To czy operator jest lewostronny, prawostronny czy obustronny decyduje o rozmieszczeniu argumentu. Jeżeli operator jest lewostronny, to dotyczy atomu znajdującego się po jego lewej stronie np.: Kar*ol – w tym wzorze operator gwiazdki dotyczy atomu litery r, (Karol)* w tym wypadku dotyczy wyrażenia atomowego w nawiasie.

Atom - jest to najmniejsze wyrażenie we wzorze. Nie rozpatrywujemy go na mniejsze składowe. Z atomów zbudowany jest wzór wyrażenia regularnego. Atomem może być pojedyncza litera lub cyfra. Dzięki nawiasom okrągłym, możemy całe wyrażenie znajdujące się wewnątrz nich przedstawić jako atom.

Wydaje się to logiczne. Jeżeli dla operatora lewostronnego podamy cały nawias, wtedy argumentami są wszystkie atomy znajdujące się wewnątrz nawiasu (może być to np. cały napis).

W artykule będę posługiwał się przede wszystkim funkcją preg_match(). Funkcje wyrażeń regularnych rozpoczynające się przedrostkiem preg pochodzą z rodziny PCRE (język Perl) i są bardziej optymalne od funkcji z ereg z rodziny POSIX. Tak podaje dokumentacja i wiele innych źródeł dostępnych w internecie.

Działanie funkcji Preg_match

Funkcja Preg_match() jest podstawową funkcją PHP dotyczącą wyrażeń regularnych. Przyjmuje ona 2 argumenty: wzorzec oraz ciąg znaków. Jeżeli ciąg znaków zostanie pomyślnie dopasowany do wzorca – zwraca wartość logiczną true, w przeciwnym razie false.

int preg_match (string $wzorzec, string $ciag_znakow)

Przekazując wzorzec do funkcji należy pamiętać aby umieścić go pomiędzy dwoma slashami. Przykładowe wywołanie funkcji może wyglądać następująco:

<?php
    if (preg_match('/abc/', 'abcdabcdabcd'))
    {
        echo "Wzorzec 'abc' pasuje do ciągu znaków 'abcdabcdabcd'";
    }
    else
    {
        echo "Błąd: Wzorzec nie pasuje do ciągu znaków";
    }
?>

Praktyczne wykorzystanie operatorów

Przyjrzyjmy się jak działa kod umieszczony w poprzednim akapicie. Prosty wzorzec abc pasuje do ciągu znaków abcdabcdabcd. Co więcej, został on dopasowany w wielu miejscach:

php-regex-1

Posłużmy się operatorem początku ciągu znaków (czyli ^). Dopiszmy go do naszego wzorca ^abc i porównajmy do tego samego ciągu znaków:

php-regex-2

Tym razem wzorzec został dopasowany tylko jeden raz. Zamiast operatora  początku ciągu można także użyć operator końca. Można także użyć obydwu operatorów na raz. Po zmianie operatorów wzorzec nie zostanie dopasowany (chyba że zmienimy ciąg znaków). Przyjrzyjmy się obrazkowi:

php-regex-3

Operatory początku i końca dają małe możliwości. Aby lepiej zrozumieć wyrażenia regularne, zróbmy kilka testów z operatorem dowolnego znaku ‚.‚ i operatorami powtórzeń.

Operator kropki . zastępuje jeden dowolny znak. Nie można określić czy jest to operator lewostronny czy prawostronny, ponieważ nie przyjmuje on argumentów. Używając operatorów powtórzeń, należy pamiętać, że dotyczą one atomu znajdującego się po lewej stronie operatora. Spójrz na przykład:

php-regex-4

Czas na operatory powtórzeń. Użyję *, ? oraz +. Ich opis znajdziesz w tabelce na samej górze artykułu. Są to operatory lewostronne, czyli najważniejszy jest atom znajdujący się po lewej stronie operatora. Pamiętaj że atomem może być także cały nawias:

php-regex-5

Robi się coraz ciekawiej. Proponują przetestować nawiasy oraz grupy znaków [a-z], [A-Z] i [0-9]. Wszystko odbywa się w ten sam sposób, należy tylko zapamiętać, że nawias jest traktowany jako jeden znak czyli atom. Grupy znaków w nawiasach kwadratowych także traktujemy jako jeden atom.

php-regex-6

Na obrazku wyżej, ostatni przykład musi zaczynać się od przynajmniej jednej dużej litery. Następnie maksymalnie jeden raz może pojawić się litera a. Następnie wiele razy może się powtórzyć końcówka rol. Napis musi kończyć się końcówką rol.

Ostatnimi operatorami są operatory szczegółowego powtarzania (nawiasy klamrowe) oraz operator alternatywy. Alternatywa to funkcja logiczna wybierająca pomiędzy argumentami. Działanie jest bardzo proste:

php-regex-7

Wszystkie operatory zostały przedstawione. Teraz wystarczy trochę poćwiczyć, aby nabrać wprawy. Warto nauczyć się ich na pamięć.Dobrze opanowane wyrażenia regularne dają duże pole do popisu, każdemu kto programuje w PHP.

Walidacja adresu email za pomocą wyrażeń regularnych

Pisanie długich wyrażeń regularnych najlepiej rozbić na kilka osobnych części. Na końcu każdą część zamykamy w nawias tworząc atom i łączymy je innym atomem bądź operatorem.

Najbardziej przydanym i najczęściej stosowanym przypadkiem używania wyrażeń regularnych jest walidacja adresu email. Każdy większy serwis sprawdza poprawność wprowadzonego adresu email. Poprawny adres ma format: login@serwer.domena.

Pisząc wyrażenie regularne walidujące adres emailowy, mamy na prawdę dużo możliwości. Możemy ustalić jakiej długości ma być login, czy adres może zawierać cyfry i czy może zaczynać się od cyfry. Możemy ustalić minimalną długość serwera albo dopuszczalne nazwy serwerów (np.: gmail, interia i o2). Możemy także ustalić domenę i dopuszczać w rejestracji tylko końcówki .pl.

Zacznijmy od loginu. Przyjmuję, że login w adresie email powinien mieć długość od 4 do 20 znaków. Dopuszczam w nim wszelkie znaki. Całość łączę w nawias:

Wzorzec walidacji loginu: ([a-z|A-Z|0-9]{4,20})

Między loginem a serwerem znajduje się małpa, ale ten znak dodamy na końcu łącząc poszczególne atomy. Czas na serwer. Przyjmijmy, że powinien mieć on długość od 2 do 10 znaków. Wszystko będzie analogicznie jak w przykładzie wyżej, zmieni się tylko ilość znaków:

Wzorzec walidacji serwera: ([a-z|A-Z|0-9]{2,10})

Między loginem a domeną musi znajdować się kropka, dodamy ją na samym końcu podczas łączenia atomów. Ostatnim krokiem jest domena. Mogli byśmy ograniczyć ją od 2 do 3 znaków, jednak na potrzeby kursu przyjmuję, że chcemy adresy email Polaków, Niemców oraz domeny „.com”. Posłużymy się operatorem alternatywy:

Wzorzec walidacji domeny: (pl|gr|com)

Ostatnim krokiem jest połączenie wszystkich atomów w jeden wzorzec. Nie można zapomnieć o znaku początku i końca ciągu znaków. Uwaga! Ponieważ kropka jest operatorem wyrażeń regularnych, a chcemy użyć znaku kropki jako oddzielenie serwera od domeny, dlatego poprzedzamy ją dwoma backslashami. Ostateczna postać wyrażenia:

Walidacja adresu email: /^([a-z|A-Z|0-9]{4,20})@([a-z|A-Z|0-9]{2,10})\\.(pl|gr|com)$/

<?php
    if (isset($_GET['email']))
    {
        $email = $_GET['email'];
        $wynik = preg_match('/^([a-z|A-Z|0-9]{4,20})@([a-z|A-Z|0-9]{2,10})\\.(pl|gr|com)$/', $email);
        
        if ($wynik)
        {
            echo "Wpisany adres jest poprawny! :)";
        }
        else echo "Błąd: proszę wpisać poprawny adres email!";
    
    }
?>

Standardowo nie wklejam żadnych gotowców. Pełno gotowych rozwiązań można znaleźć w internecie.