logo
close

Wyrażenia regularne

3 marca 2019
simplie Komentarze: 0 Kategoria: Kurs JavaScript

Wprowadzenie

Wyrażenia regularne (po angielsku Regular Expressions) są sposobem na zapisanie wzorca, z którym mogą być porównywane inne stringi. Mogą one być używane do sprawdzania czy dany ciąg znaków pasuje do podanego wzorca (np. czy kod pocztowy składa się kolejno z dwóch cyfr, myślnika i trzech cyfr), do wyszukiwania podciągów i ich ewentualnej zamiany na inny ciąg znaków. Proste reguły pozwalają na tworzenie wyrażeń regularnych opisujących reguły budowy nawet bardziej złożonych ciągów znaków. Dzięki temu wyrażenia regularne zdobyły popularność i są szeroko stosowane – nie tylko w JavaScript, ale też w większości obecnie stosowanych języków programowania. Dlatego jeżeli nauczysz się stosować wyrażenia regularne w JavaScript, bez problemu nauczysz się ich stosowania w innych językach (np. w PHP). Jedyne różnice jakie mogą wystąpić będą dotyczyć bardziej złożonych konstrukcji, gdyż podstawy wszędzie są takie same.

Za obsługę wyrażeń regularnych w JavaScript odpowiada klasa RegExp. Klasę tą opiszę w kolejnej lekcji tego kursu, poświęconej zaawansowanym mechanizmom pracy ze stringami. Na potrzeby tej lekcji pokażę jedynie jak zbudować prosty formularz pozwalający na testowanie wyrażeń regularnych:

Wyrażenie regularne:<br />
<input type="text" id="rx" size="80" value="^\d{2}-\d{3}$" /><br />
Sprawdzany ciąg znaków:<br />
<input type="text" id="str" size="80" value="12-345" /><br />
<button onclick="alert(new RegExp(document.getElementById('rx').value)
    .exec(document.getElementById('str').value))">Sprawdź!</button>

Wyrażenie regularne:

Sprawdzany ciąg znaków:

Do powyższych dwóch pól możesz wpisać wyrażenie regularne oraz tekst który ma być z nim porównany. Po kliknięciu przycisku „Sprawdź!” utworzony zostanie obiekt wyrażenia regularnego i zostanie wywołana na nim metoda exec(). Jeżeli wyrażenie zostanie dopasowane do podanego ciągu, metoda ta zwróci tablicę zawierającą dopasowany ciąg znaków, i ew. jako kolejne elementy inne dopasowane podciągi (więcej na ten temat dalej). Elementy tablicy przy wyświetlaniu będą rozdzielone przecinkami. Jeżeli nie będzie dopasowania, metoda zwróci wartość null, która też będzie wyświetlona. W przypadku błędu w wyrażeniu regularnym uniemożliwiającym jego kompilację (np. brakujący nawias) wystąpi błąd w skrypcie – ta sytuacja nie jest sprawdzana :).

Podstawy wyrażeń regularnych

Podstawowymi składowymi wyrażeń regularnych są znaki. Każdy znak dopasowuje się do odpowiadającego mu znaku w ciągu znaków – tak jest dla wszystkich znaków z wyjątkiem znaków specjalnych, wymienionych poniżej:

( ) [ ] { } ^ $ . ? + * \ |

Aby dopasować się do jednego z tych znaków, trzeba go w treści wyrażenia regularnego poprzedzić znakiem backslash („\”). Ważna jest też wielkość znaków – domyślnie wielkość liter ma znaczenie (ale można to wyłączyć).

Przykładowo wyrażenie regularne składające się z trzech liter „abc” dopasuje się to tych trzech liter w sprawdzanym ciągu znaków.

Grupy znaków

Oprócz pojedynczych znaków można też określić że w danym miejscu może wystąpić dowolny znak. Do tego celu służy znak kropki („.”) – zastępuje on dowolny inny znak (zazwyczaj z wyjątkiem znaku nowej linii, „\n”). Dla przykładu poniższe wyrażenie regularne może dopasować się do imion Ala, Ola, Ula, Ela:

.la

Można też podać listę znaków które mogą pojawić się na danej pozycji, poprzez wymienienie ich wewnątrz nawiasów kwadratowych. Przykładowo w taki sposób można określić że po literze „a” ma się pojawić cyfra:

a[0123456789]

Istnieje też wersja skrócona, pozwalająca na podanie wszystkich znaków z określonego zakresu. Stosując ją podaje się pierwszy i ostatni znak z zakresu, rozdzielając je myślnikiem. Poniższy przykład jest równoważny temu poprzedniemu:

a[0-9]

Wewnątrz nawiasów klamrowych można równocześnie podać kilka różnych zakresów znaków oraz wymieniać pojedyncze znaki. Uważać należy jedynie na znaki minusa oraz daszek („^” – o nim za chwilę) – jeżeli potrzebujesz ich użyć, poprzedź je znakiem backslash („\”). Zazwyczaj też działa umieszczenie znaku minus jako ostatniego znaku w nawiasach kwadratowych, a daszka – gdzieś poza pierwszą pozycją.

[0-9a-fxyz^-] [0123a-ce-jyz\^\-]

Możliwe jest też zanegowanie listy, czyli określenie jakie znaki nie mogą się znaleźć na danej pozycji. Do tego celu służy znak daszka („^”), który należy umieścić jako pierwszy w nawiasach klamrowych. Przykładowo w taki sposób można dopasować się do dowolnego znaku który nie jest cyfrą ani literą:

[^0-9a-zA-Z]

Dostępne są także specjalne sekwencje backslash-litera, zastępujące predefiniowane zestawy znaków:

  • \d – dowolna cyfra: [0-9];
  • \D – dowolny znak nie będący cyfrą: [^0-9];
  • \w – dowolna cyfra, litera (mała lub duża) lub znak podkreślenia: [0-9a-zA-Z_];
  • \W – dowolna znak nie będący cyfrą, literą (małą lub dużą) lub znakiem podkreślenia: [^0-9a-zA-Z_];
  • \s – dowolny biały znak: [ \t\r\n\v\f];
  • \S – dowolny nie-biały znak: [^ \t\r\n\v\f].

Powyżej użyłem kilku innych znaków specjalnych – oto ich znaczenie:

  • \t – znak tabulacji poziomej (kod 0x09);
  • \r – znak powrotu karetki (kod 0x0D);
  • \n – znak nowej linii (kod 0x0A);
  • \v – znak tabulacji pionowej (kod 0x0B);
  • \f – znak wysunięcia strony (kod 0x0C).

Powtórzenia wzorca

W trakcie pisania wyrażeń regularnych często zdarza się sytuacja gdy pewien fragment wzorca powinien się powtórzyć. Aby to umożliwić, istnieją specjalne kwantyfikatory, które umieszczone w wyrażeniu regularnym określają ile razy może (lub ma) się powtórzyć element znajdujący się tuż przed nim. Podstawowe trzy to znaki pytajnika, gwiazdki i plusa:

  • ? – określa że poprzedzający element jest opcjonalny (może wystąpić 0 lub 1 raz);
  • * – określa że poprzedzający element może wystąpić zero lub więcej razy;
  • + – określa że poprzedzający element może wystąpić jeden lub więcej razy.

Można ich użyć np. w taki sposób:

ab?c+d*

Powyższe wyrażenie regularne dopasuje się do ciągu znaków, który zaczyna się od litery „a”, po niej może znaleźć się jedna litera „b” (jest ona opcjonalna), następnie jest co najmniej jedna litera „c”, po której (lub których) może znaleźć się dowolna ilość liter „d”.

Często stosuje się też poniższy zapis, który dopasowuje się do dowolnego (także pustego) ciągu znaków:

.*

Wyrażenia regularne dostępne w JavaScript pozwalają na użycie dodatkowych sposobów na określenie ile razy ma się powtórzyć dany element. Oto one:

  • {n} – określa że element ma się powtórzyć dokładnie n razy;
  • {n,} – określa że element ma się powtórzyć co najmniej n razy;
  • {n,m} – określa że element ma się powtórzyć co najmniej n razy, ale nie więcej niż m razy (czyli od n do m razy).

Wymienione wcześniej trzy kwantyfikatory (pytajnik, gwiazdka i plus) można inaczej tak zapisać stosując powyższą składnię z nawiasami klamrowymi:

  • ? – {0,1}
  • * – {0,}
  • + – {1,}

Stosując te kwantyfikatory, można w prosty sposób napisać wyrażenie regularne opisujące kod pocztowy:

[0-9]{2}-[0-9]{3}

Powyższy zapis można jeszcze uprościć w taki sposób:

\d{2}-\d{3}

Wszystkie powyższe kwantyfikatory określające ilość powtórzeń są określane jako „zachłanne”, co oznacza że będą próbowały dopasowywać się do jak najdłuższych podciągów znaków. Czasami może to powodować problemy – popatrz na to wyrażenie regularne:

<.*>

W założeniu ma się ono dopasować do pojedynczego znacznika HTML. W praktyce tak jednak nie jest. Jeżeli użyjesz go na takim ciągu kodu HTML:

aaa <br> bbb <br> ccc

to dopasuje się on to takiego podciągu:

<br> bbb <br>

Takie dopasowanie wynika z faktu że użyte kwantyfikatory są „zachłanne”. Aby naprawić ten problem, można zmodyfikować to wyrażenie aby określić jakie znaki mogą znaleźć się pomiędzy nawiasami trójkątnymi:

<[^>]*>

Druga metoda to użycie kwantyfikatorów, które nie są „zachłanne” (inna nazwa to kwantyfikatory leniwe). Są to wersje poprzednich kwantyfikatorów, do których dopisany jest dodatkowy pytajnik: ??*?+?{n}?{n,}? i {n,m}?. Stosując je, można ostatnie wyrażenie zapisać w następujący sposób:

<.*?>

Zakotwiczenia

Wyrażenia regularne domyślnie dopasowują się do pewnego podciągu. Przed tym podciągiem i za nim mogą się znajdować inne znaki, które nie dopasowały się do użytego wyrażenia regularnego. Często jest to niepożądane, np. gdy stosujemy wyrażenie regularne do sprawdzenia poprawności wprowadzonych danych. W takiej sytuacji należy takie wyrażenie regularne „zakotwiczyć”. Do tego celu stosuje się dwa znaki specjalne: daszek („^”) i dolar („$”). Pierwszy z nich (daszek) dopasowuje się do początku ciągu, a drugi (dolar) do końca ciągu. Dlatego sprawdzając np. poprawność kodu pocztowego, należy napisać takie wyrażenie regularne:

^\d{2}-\d{3}$

Te dwa znaki specjalne są od siebie niezależne. Można zatem pisać wyrażenia regularne które będą stosować tylko jeden z nich – przykładowo wyrażenie regularne sprawdzające czy string rozpoczyna się od trzech liczb, rozdzielonych średnikami:

^\d+;\d+;\d+;

Istnieją jeszcze dwa inne zakotwiczenia: \b i \B. Pierwsze z nich (\b) dopasowuje się do granicy słowa (czyli grupy znaków mogącej składać się z liter, cyfr i znaku podkreślenia). Drugi z nich (\B) natomiast dopasowuje się do miejsc gdzie nie ma granicy słowa (czyli np. pomiędzy literami w słowie, albo pomiędzy spacjami).

Grupy i wersje alternatywne

Powyższe przykłady pokazywały jak określić ilość powtórzeń pojedynczych znaków. Często jednak zdarza się sytuacja gdzie zamiast pojedynczych znaków powtarzać się ma grupa znaków. Aby coś takiego osiągnąć, należy dany ciąg znaków otoczyć okrągłymi nawiasami. Przykładowo tak będzie wyglądać wyrażenie regularne dopasowujące się do powtórzonego wielokrotnie ciągu znaków „abc”:

(abc)+

Grupy można też oczywiście zagnieżdżać:

(abc(123)?)+

Powyższy przykład opisuje ciąg znaków, w którym po literach „abc” mogą znaleźć się cyfry „123” (są one opcjonalne). Cały taki podciąg (składający się z tych liter i cyfr) może się powtórzyć jeden lub więcej razy.

Obok grupowania czasami przydaje się też możliwość podania kilku różnych wersji ciągu, z których tylko jedna się ostatecznie dopasuje. Aby to zrobić, należy te wersje rozdzielić znakiem pionowej kreski „|”:

aaa (bbb|ccc|ddd)

Warto tutaj zwrócić uwagę na takie wyrażenie regularne:

^aaa|bbb$

Wyrażenie to dopasuje się do ciągu który rozpoczyna się od „aaa”, albo który kończy się na „bbb”. Jeżeli chcesz napisać wyrażenie które dopasuje się dokładnie do jednego z tych dwóch ciągów, to powinno ono wyglądać tak:

^(aaa|bbb)$

Nawiasy okrągłe są także stosowane do pobierania wartości wybranych podciągów w trakcie dopasowywania wyrażenia regularnego. Są one zwracane jako kolejne elementy tablicy zwracanej przez wywołanie metody RegExp.exec(). Jeżeli celem stosowania nawiasów jest tylko grupowanie i nie chcesz aby podciąg dopasowany do zawartości nawiasu nie był zwracany, dopisz po otwierającym nawiasie okrągłym pytajnik i dwukropek:

^(?:aaa|bbb)$

Odwołania wsteczne

Podciągi dopasowane do fragmentów wyrażenia regularnego umieszczonych w nawiasach są nie tylko zwracane na zewnątrz, ale można się też do nich odwołać z innych fragmentów wyrażenia regularnego. Robi się to poprzez umieszczenie w wybranym miejscu backslasha („\”) po którym wstawiamy cyfrę określającą numer kolejny nawiasu – fragmentu wyrażenia, do którego chcemy się odwołać. W ten sposób można napisać wyrażenie regularne sprawdzające np. czy dany fragment wyrażenia powtarza się w innym miejscu. Przykładowo to wyrażenie regularne sprawdzi, czy wśród wartości rozdzielonych średnikami zdarzy się sytuacja, że dwie kolejne wartości będą identyczne:

;([^;]+);\1;

Spojrzenie w przód

Stosując wyrażenia regularne w JavaScript można użyć jeszcze dwóch dodatkowych funkcji. Służą one do sprawdzenia czy w analizowanym ciągu znaków począwszy od pewnego miejsca znajduje się inne wyrażenie, bez jego zwracania jako części podstawowego dopasowania. W drugiej wersji jest to sprawdzenie czy określone wyrażenie nie występuje.

Aby sprawdzić czy dany fragment tekstu występuje w dalszej części ciągu, należy odpowiadające mu wyrażenie regularne umieścić wewnątrz następującej konstrukcji: (?= ). Przykład wykorzystania tej funkcji to poniższe wyrażenie – zastosowane do ciągu znaków „0testabc1testxyz” dopasowuje się do ciągu znaków „0test”:

\d[a-z]{4}(?=abc)

Odwrotnością tej funkcji jest sprawdzenie czy dany ciąg znaków nie występuje w dalszej części analizowanego ciągu znaków. Wyrażenie regularne opisujące ciąg znaków którego ma nie być umieszcza się wewnątrz (?! ). Jeżeli lekko zmodyfikujemy powyższy przykład (patrz poniżej) i spróbujemy go dopasować do tego samego ciągu znaków co poprzednio, to tym razem dopasuje się ono do fragmentu „1test”.

\d[a-z]{4}(?!abc)

Komentarze

Komentarz

Komentując, akceptujesz Politykę prywatności