Wyrażenia regularne to potężne narzędzie do przeszukiwania danych tekstowych. W języku PHP dostępny jest zestaw funkcji umożliwiających wyszukiwanie i przetwarzanie łańcuchów znaków na podstawie wzorców reprezentowanych przez te wyrażenia. Czy wiesz, jak się nimi posługiwać? Jeśli nie, to w tym rozdziale kursu poznasz podstawy wyrażeń regularnych i dowiesz się, jak ich używać za pośrednictwem specjalnych funkcji PHP.
W tym rozdziale poznasz odpowiedzi między innymi na następujące pytania:
- Czym są wyrażenia regularne PHP?
- Jakie wyrażenia regularne są obsługiwane przez PHP?
- Jakie są podstawowe elementy wyrażeń regularnych w PHP?
- Jak tworzyć wyrażenia regularne do przeszukiwania i przetwarzania tekstu w PHP?
- Jakie funkcje PHP służą do pracy z wyrażeniami regularnymi?
Wyrażenia regularne w PHP — wprowadzenie
Język PHP obsługuje, choć może lepiej byłoby napisać, że obsługiwał, dwa rodzaje wyrażeń regularnych — POSIX i PCRE (ang. Perl Compatible Regular Expressions). Obecnie standardem w PHP są wyrażenia regularne PCRE, które są wydajniejsze i dają szersze możliwości od wyrażeń POSIX.
Wyrażeń regularnych typu POSIX nie należy używać w nowych projektach, ponieważ wszystkie funkcje do ich obsługi, czyli funkcje o nazwach zaczynających się od ereg, zostały usunięte z języka w wersji PHP 7.0.
Dlatego skupiam się tutaj tylko na podstawach wyrażeń regularnych typu PCRE w PHP. A czym dokładnie one są?
PCRE to biblioteka programistyczna napisana w języku C. Jak można domyślić się po jej nazwie (Wyrażenia regularne kompatybilne z językiem Perl), zapewnia ona obsługę wyrażeń regularnych o takiej samej składni i semantyce, jak w języku Perl, który jest uznawany za wzorowy w tej dziedzinie.
Krótko mówiąc, jeśli chcesz nauczyć się operować wyrażeniami regularnymi w języku PHP, zapoznaj się z ich odmianą w wydaniu biblioteki PCRE. Znajomość przestarzałych wyrażeń regularnych typu POSIX może Ci się przydać tylko, jeśli zamierzasz pracować ze starym kodem PHP.
Tyle tytułem wstępu. Pora przejść do spraw praktycznych.
Czym jest wyrażenie regularne
Wyrażenie regularne (ang. regular expression lub w skrócie regex) to specjalny łańcuch znaków reprezentujący pewien wzorzec tekstowy, który chcielibyśmy znaleźć w tekście. Na przykład wzorzec kot pozwalałby znaleźć w tekście wszystkie wystąpienia ciągu znaków kot.
To jednak jest najprostszy możliwy przykład, do którego tak naprawdę wcale nie potrzeba wyrażeń regularnych. Prawdziwa potęga tego narzędzia tkwi w ich znakach specjalnych i kombinacjach znaków o specjalnym znaczeniu.
Powiedzmy na przykład, że chcemy znaleźć wszystkie nazwy funkcji PHP do pracy z wyrażeniami regularnymi. Wiemy, że nazwa każdej z nich zaczyna się od słowa preg i kończy się nawiasem okrągłym — ().
To znaczy, że potrzebujemy wzorca, który będzie reprezentował wszystkie fragmenty tekstu zaczynające się od słowa preg i kończące się nawiasem (). Jego ogólna struktura ma być taka:
pregdowolne_znaki()
preg— ciąg znaków, od którego ma się zaczynać szukany tekst.- dowolne_znaki — dowolna liczba dowolnych znaków, stanowiących dalszą część nazwy funkcji.
()— ciąg znaków, którym ma się kończyć szukany tekst.
Do tego wzorca pasują na przykład takie fragmenty tekstu:
- preg()
- preg_match()
- preg_match_all()
- preg_replace_callback()
Natomiast te nie będą pasować:
- preg_match
- ereg_match()
- preg_replace_callback(
No dobrze, to jak w takim razie powinno wyglądać prawdziwe wyrażenie regularne znajdujące fragmenty tekstu według opisywanego wzorca? O tak:
/preg\w*\(\)/Na pierwszy rzut oka to wyrażenie może się wydawać skomplikowane, ale w rzeczywistości takie nie jest. Rozłożymy je na czynniki pierwsze:
- Znak
/na początku i na końcu — oznacza początek i koniec wyrażenia regularnego. Pełni podobną funkcję, jak znaczniki<?phpi?>dla skryptów PHP.Co ciekawe, w tej roli można używać praktycznie dowolnego znaku niebędącego literą ani cyfrą, a więc wszystkie poniższe przykłady są poprawne i znaczą dokładnie to samo:
#preg\w*\(\)# !preg\w*\(\)! *preg\w*\(\)* ^preg\w*\(\)^ %preg\w*\(\)% preg— ciąg znaków, który reprezentuje sam siebie, czyli oznaczający, że szukane przez nas fragmenty tekstu muszą zaczynać się od ciągu znakówpreg.\w— reprezentuje dowolną literę, cyfrę lub znak podkreślenia.*— oznacza, że to co znajduje się przed nim, może wystąpić zero lub więcej razy.\(\)— reprezentuje nawias(). Nawiasy w wyrażeniach regularnych mają specjalne znaczenie, więc jeśli chcemy ich użyć w znaczeniu dosłownym, musimy je poprzedzać ukośnikiem wstecznym.
Skoro utworzyliśmy wyrażenie regularne, to chcielibyśmy go teraz użyć w skrypcie. W języku PHP dostępnych jest kilka funkcji do pracy z wyrażeniami regularnymi typu PCRE.
Funkcje PHP do pracy z wyrażeniami regularnymi
Do najczęściej używanych funkcji PHP do pracy z wyrażeniami regularnymi typu PCRE należą funkcje preg_match() i preg_match_all().
Pierwsza z nich zwraca pierwszy znaleziony fragment tekstu pasujący do wzorca, a druga zwraca wszystkie znalezione fragmenty tekstu pasujące do wzorca, w formie tablicy. Zobaczmy więc, jak użyć naszego wyrażenia regularnego w funkcji preg_match_all().
Aby był prawdziwy pożytek z wywołania tej funkcji, trzeba jej przekazać trzy argumenty w takiej kolejności, jak je wymieniam: wyrażenie regularne, tekst do przeszukania i tablica, w której mają zostać zapisane wyniki. Na przykład:
$pcre_functions = 'preg_match(), preg_match_all(), preg_match, preg_replace_callback(), ereg_match(), preg_replace_callback(, preg)';
preg_match_all("/preg\w*\(\)/", $pcre_functions, $matches);
print_r($matches);Wynik:
Array
(
[0] => Array
(
[0] => preg_match()
[1] => preg_match_all()
[2] => preg_replace_callback()
)
)
Łańcuch $pcre_functions zawiera kilka nazw funkcji PHP PCRE, z których część jest nieprawidłowo sformatowana. Nasze wyrażenie regularne zwróciło nam tylko te, które są zapisane prawidłowo.
preg_match_all() można wywołać także bez trzeciego argumentu, ale wtedy nie będzie sposobu, aby dostać się do znalezionych elementów. Jedynie będzie można dowiedzieć się na podstawie jej wartości zwrotnej, ile elementów znalazła.Funkcje PHP preg_match_all() i preg_match() należą do najczęściej używanych, ale poza nimi jest jeszcze kilka innych. Poniższa tabela zawiera zestawienie wszystkich funkcji PHP obsługujących wyrażenia regularne typu PCRE.
| Funkcja PHP PCRE | Opis |
|---|---|
preg_filter() |
Wykonuje wyszukiwanie i zamianę tekstu na podstawie wzorca w formie wyrażenia regularnego |
preg_grep() |
Zwraca wartości tablicy pasujące do wzorca |
preg_last_error() |
Zwraca kod błędu ostatniego wykonania wyrażenia regularnego |
preg_last_error_msg() |
Zwraca komunikat o błędzie ostatniego wykonania wyrażenia regularnego |
preg_match() |
Znajduje pierwszy fragment tekstu pasujący do wzorca |
preg_match_all() |
Znajduje wszystkie fragmenty tekstu pasujące do wzorca |
preg_quote() |
Dodaje ukośnik przed każdym znakiem należącym do składni wyrażeń regularnych |
preg_replace() |
Wykonuje wyszukiwanie i zamianę tekstu na podstawie wzorca w formie wyrażenia regularnego |
preg_replace_callback() |
Wykonuje wyszukiwanie i zamianę tekstu na podstawie wzorca w formie wyrażenia regularnego przy użyciu funkcji zwrotnej |
preg_replace_callback_array() |
Wykonuje wyszukiwanie i zamianę tekstu na podstawie wzorca w formie wyrażenia regularnego przy użyciu funkcji zwrotnych |
preg_split() |
Dzieli łańcuch na podstawie dopasowania do wyrażenia regularnego |
Teraz przyjrzymy się kilku najważniejszym elementom składni wyrażeń regularnych. Nie jest to kurs tej technologii, więc przedstawiam tylko podstawowe wiadomości. Jeśli chcesz poznać ją szczegółowo, to polecam książkę Wyrażenia regularne, Jeffreya E. F. Friedla wydawnictwa Helion lub, jeśli znasz język angielski, kurs wyrażeń regularnych dostępny pod adresem https://www.regular-expressions.info/
Zaczniemy od poszerzenia wiedzy na temat elementów użytych w przykładowym wyrażeniu. Na pierwszy ogień weźmiemy znaki, które w wyrażeniach regularnych mają specjalne znaczenie.
Metaznaki wyrażeń regularnych PCRE
W wyrażeniach regularnych typu PCRE jest 12 podstawowych metaznaków, czyli znaków, które w wyrażeniach regularnych nie reprezentują samych siebie, tylko mają specjalne znaczenie. W poniższej tabeli przedstawiam ich zwięzły opis, a w kolejnych sekcjach znajdziesz dokładniejsze objaśnienia.
| Metaznak PCRE | Opis |
|---|---|
$ |
Koniec łańcucha znaków lub wiersza |
^ |
Początek wiersza lub negacja |
( |
Początek grupy |
) |
Koniec grupy |
[ |
Klasa znaków |
* |
Zero lub więcej |
. |
Dowolny znak z wyjątkiem znaku nowego wiersza |
? |
Zero lub jeden |
\ |
Razem z następnym znakiem tworzy sekwencję specjalną lub oznacza, że następny znak należy traktować dosłownie |
{ |
Liczba powtórzeń |
| |
Lub |
+ |
Jeden lub więcej |
W większości przypadków użycie któregokolwiek z tych znaków samodzielnie jest błędem. A jeśli chcesz użyć któregoś z nich dosłownie, musisz poprzedzić go ukośnikiem wstecznym. W przykładowym wyrażeniu regularnym mamy fragment \(\), który reprezentuje otwarcie i zamknięcie nawiasu, a nie jakąkolwiek grupę.
Kotwice PCRE
Dwa pierwsze znaki w tablicy Podstawowe metaznaki PCRE PHP ($ i ^) to tak zwane kotwice. Nie odnoszą się one do żadnych znaków ani grup znaków, tylko do określonych miejsc wśród znaków.
Znak $ oznacza koniec łańcucha znaków PHP lub wiersza, a znak ^ — początek.
Na przykład wyrażenie /^d/ pasuje do litery d w ciągu znaków 'dzban', ponieważ litera ta znajduje się na początku. Natomiast dla wyrażenia /^z/ nie zostałoby znalezione dopasowanie w tym ciągu, ponieważ w nim litera z nie znajduje się na początku.
Analogicznie jest ze znakiem $, który pasuje tylko wtedy, gdy szukany wzorzec znajduje się na końcu łańcucha znaków. Na przykład wyrażenie /n$/ znajdzie literę n na końcu ciągu znaków 'dzban', natomiast /a$/ w tym przypadku nic nie znajdzie, ponieważ na końcu tego ciągu nie ma litery a.
Znaki $ i ^ odnoszą się nie tylko do pierwszej litery, ale do całego fragmentu, który znajduje się bezpośrednio przed lub za nimi. Może to być nawet fragment wzorca, czyli grupa.
Oprócz tych dwóch kotwic istnieje jeszcze kilka innych. Na przykład \b oznacza granicę słowa, czyli miejsce pomiędzy znakiem, który może należeć do słowa (\w), a znakiem, który nie może należeć do słowa (\W), a \B oznacza dowolne miejsce, które nie stanowi granicy słowa.
Grupowanie PCRE
Czasami w tekście trzeba wyszukać ciągi składające się z kilku grup znaków, np. kod pocztowy składa się dwóch cyfr, łącznika i kolejnych trzech cyfr — czyli można powiedzieć, że składa się z trzech grup znaków. Do przedstawienia takiego wzorca z grupami w wyrażeniach regularnych PCRE można wykorzystać nawiasy, np.:
$post_codes = '01-153 03 87 kjmsda 08-300 08-600, 02-240';
preg_match_all('/(0|1|2|3|4|5|6|7|8|9)(0|1|2|3|4|5|6|7|8|9)-(0|1|2|3|4|5|6|7|8|9)(0|1|2|3|4|5|6|7|8|9)(0|1|2|3|4|5|6|7|8|9)/', $post_codes, $matches);Tablica $post_codes zawiera kilka kodów pocztowych i parę nieprawidłowych wartości, aby pokazać, że zostaną odrzucone.
Wyrażenie regularne zawiera aż pięć grup wyrażeń — na początku musi być dowolna cyfra, po niej musi być druga cyfra, następnie musi być łącznik, a po nim muszą znajdować się jeszcze trzy cyfry.
|, który oznacza, że może zostać użyty jeden z wymienionych elementów.Wynik w tym przypadku będzie następujący:
Array
(
[0] => Array
(
[0] => 01-153
[1] => 08-300
[2] => 08-600
[3] => 02-240
)
)
Jednak wypisywanie wszystkich cyfr za każdym razem jest strasznie żmudne. Dlatego w wyrażeniach regularnych dostępne są tak zwane klasy znaków, czyli skrótowe formy określania całych grup znaków, np. wszystkich cyfr albo znaków alfabetu.
Klasy znaków PCRE
Poprzednie przykładowe wyrażenie regularne niby działa, ale można je znacznie ulepszyć, czyli skrócić. Zamiast pięć razy wymieniać wszystkie cyfry dziesiętne i przeplatać je metaznakiem | w nawiasie okrągłym, można zdefiniować ich zakresy w nawiasie kwadratowym, na przykład:
$post_codes = '01-153 03 87 kjmsda 08-300 08-600, 02-240';
preg_match_all('/[0-9][0-9]-[0-9][0-9][0-9]/', $post_codes, $matches);Wynik będzie taki sam, jak w poprzedniej wersji, ale wyrażenie jest znacznie krótsze i bardziej przejrzyste.
W tym przykładzie pokazałem jeden z kilku sposobów definiowania klas znaków w wyrażeniach regularnych PCRE. Metoda ta dobrze sprawdza się, kiedy chcemy określić pewien zakres znaków, np. cyfry od 2 do 7 ([2-7]) albo litery od d do h ([d-h]).
Ewentualnie można utworzyć własną klasę znaków, wpisując wszystkie należące do niej znaki w nawiasie kwadratowym. Parę przykładów:
[abcde]— a lub b lub c lub d lub e[1234]— 1 lub 2 lub 3 lub 4[1g0]— 1 lub g lub 0
^), to będzie on oznaczał negację, czyli akceptowane będą wszystkie znaki z wyjątkiem tych, które należą do klasy.Niektóre klasy znaków są tak często używane, że utworzono dla nich standardowe skrócone formy zapisu. Przedstawiam je w poniższej tabeli.
| Klasa znaków PCRE | Opis | Przykład | Przykładowe dopasowania |
|---|---|---|---|
\d |
Cyfra dziesiętna | /[0-9]\d/ |
'01', '11', '47' |
\D |
Wszystko, co nie jest cyfrą | /[0-9]\D/ |
'0d', '1a', '3y' |
\s |
Biały znak, np. spacja, tabulator itd. | /[0-9]\s/ |
'0 ' |
\S |
Wszystko, co nie jest białym znakiem | /[0-9]\S/ |
'0a', '89', '4r' |
\w |
Dowolna mała lub wielka litera, dowolna cyfra lub znak podkreślenia | /[0-9]\w/ |
'0v', '21', '3_' |
\W |
Wszystko, co nie jest małą ani wielką literą, cyfrą ani znakiem podkreślenia | /[0-9]\W/ |
'5,', '5%', '7!' |
Przy użyciu tych klas znaków jeszcze bardziej możemy uprościć nasze przykładowe wyrażenie regularne:
preg_match_all('/\d\d-\d\d\d/', $post_codes, $matches);Wynik będzie taki sam, jak w poprzednich przypadkach.
Kolejną bardzo przydatną techniką wyrażeń regularnych PHP PCRE jest definiowanie wzorców powtórzeń.
Powtórzenia PCRE
W tabeli Metaznaki wyrażeń regularnych PHP PCRE przedstawiłem trzy metaznaki z kategorii powtórzeń: * (zero lub więcej), + (jeden lub więcej) oraz ? (zero lub jeden). Jeśli to nie wystarczy, można zdefiniować własne wzorce powtarzalności, np. „od 4 do 7” albo „9 lub więcej” itd.
Do tworzenia własnych wzorców powtarzalności służą klamry, których można używać w sposób przedstawiony w poniższej tabeli.
| Składnia | Opis | Przykład | Pasuje do |
|---|---|---|---|
{n} |
Dokładnie n razy | /[0-9]{4}/ |
'1111', '3479', '7053' |
{n,} |
Przynajmniej n razy | /[0-9]{6,}/ |
'3141592' |
{n,m} |
Od n do m razy | '/[0-9]{4,6}/' |
'3141', '07535', '137091' |
W tym rozdziale przedstawiłem tylko absolutne podstawy, które mogą być dobrym wprowadzeniem do wyrażeń regularnych PCRE w PHP. Jeśli jednak chcesz na poważnie posługiwać się tym narzędziem, to koniecznie musisz się jeszcze sporo douczyć. Zachęcam do skorzystania ze źródeł podanych w sekcji Zobacz również.
Podsumowanie
- Wyrażenia regularne to wzorce tekstowe służące do zaawansowanego przeszukiwania tekstu.
- W starszych wersjach PHP obsługiwane były wyrażenia regularne typu POSIX i PCRE.
- Od PHP 7.0.0 obsługiwane są tylko wyrażenia regularne PHP PCRE.
- W wyrażeniach regularnych dostępny jest zestaw znaków specjalnych, zwanych metaznakami.
- Kotwice to metaznaki odnoszące się do miejsca w łańcuchu.
- Do grupowania znaków w wyrażeniach regularnych typu PCRE służą nawiasy okrągłe.
- Klasy znaków w wyrażeniach regularnych typu PCRE można tworzyć przy użyciu nawiasów kwadratowych.
- Liczbę powtórzeń wybranych znaków można określić za pomocą metaznaków *, + i ? oraz przy użyciu składni klamrowej.
Pytania
- Czym są wyrażenia regularne PCRE?
- Którego typu wyrażeń regularnych powinno się używać w nowych skryptach w języku PHP?
- Czym są metaznaki w wyrażeniach regularnych typu PCRE?
- Jakie znasz metaznaki w wyrażeniach regularnych typu PCRE?
- Czym są kotwice w wyrażeniach regularnych typu PCRE?
- Na czym polega grupowanie w wyrażeniach regularnych typu PCRE?
- Czym są klasy znaków w wyrażeniach regularnych typu PCRE?
Ćwiczenia
- Objaśnij znaczenie poniższych wyrażeń regularnych:
- /.*kot.*[, ]/
- \b[a-zA-Z0-9]+_[0-9]+\b
- /\b\d{2}.\d{2}.\d{4}\b/
- Napisz wyrażenie regularne pasujące do adresów e-mail w formacie: abc@de.fg (dowolna liczba cyfr i liter, @, dowolna liczba cyfr i liter, kropka, dowolna liczba cyfr i liter).
- Zmodyfikuj wyrażenie z ćwiczenia 2. tak, aby dopuszczało tylko adresy e-mail w domenie pl).
- Zmodyfikuj wyrażenie z ćwiczenia 3. tak, aby dopuszczało tylko adresy e-mail z domeną trzyliterową składającą się wyłącznie z małych liter.
- Popraw ostatnie wyrażenie regularne z ćwiczenia 1., aby w miejscu miesiąca dopuszczało tylko wartości z przedziału od 1 do 12.
Rozwiązania ćwiczeń
-
- Pasuje do kot, blekot itd., np.:
echo preg_match("/.*kot.*[, ]/", "kot, blekot, maskotka, kotara", $matches); - Znajduje ciągi typu abc_12 (przynajmniej jedna litera lub cyfra, znak podkreślenia, przynajmniej jedna cyfra.
- Znajduje daty (a raczej ciągi znaków) w formacie mm.dd.rrrr — nie ma ograniczeń, co do wartości na poszczególnych miejscach, a więc dzień może mieć numer 98.
- Pasuje do kot, blekot itd., np.:
- /\b[\w.-]+@[\w.-]+\.\w+\b/
- /\b[\w.-]+@[\w.-]+\.pl\b/
- /\b[\w.-]+@[\w.-]+\.[a-z]{3}\b/
- /\b\d{2}.[1-12]{2}.\d{4}\b/



