W poprzedniej lekcji tego kursu opisałem składnię wyrażeń regularnych. Teraz nadszedł czas aby opisać w jaki sposób można z nich korzystać i jakie możliwości daje tutaj język JavaScript.

Obiekt RegExp

Za obsługę wyrażeń regularnych w języku JavaScript odpowiada obiekt RegExp. Obiekt ten można utworzyć na dwa sposoby. Pierwszy sposób to wykorzystując jego konstruktor, czyli podobnie jak inne obiekty). Drugi sposób natomiast jest wzorowany na języku Perl – wyrażenie regularne umieszczamy pomiędzy dwoma znakami slash (“/”). Poniższy przykład pokazuje te dwa sposoby:

var rx1 = new RegExp('^[0-9]+[a-z]+$');
var rx2 = /^[0-9]+[a-z]+$/;

Powyższe dwa przykłady są sobie równoważne. Oczywiście należny pamiętać że chcą zastosować znaki specjalne wewnątrz wyrażenia regularnego trzeba je poprzedzić znakiem slash. Znaki te to apostrof i slash w pierwszym przykładzie, oraz znak slash w drugim przykładzie.

Dodatkowo przy tworzeniu obiektu wyrażenia regularnego możliwe jest użycie specjalnych modyfikatorów. Są trzy takie modyfikatory:

  • g – włącza wyszukiwanie globalne. Oznacza to że możliwe będzie odnalezienie (i ew. zamiana – o tym dalej) nie tylko pierwszego dopasowanego ciągu znaków (tak jest domyślnie), ale też kolejnych. Dokładniej omówię to dalej;
  • i – ignorowanie wielkości znaków w trakcie porównywania liter. Podając go można uprościć nieco zapis wyrażenia regularnego, gdyż nie trzeba podawać osobno małych i wielkich znaków – wystarczy tylko jedne z nich, np. małe;
  • m – wyszukiwanie wieloliniowe. Użycie tego modyfikatora powoduje że zakotwiczenie ^ dopasuje się także do miejsca w stringu zaraz po znaku nowej linii (“\r” lub “\n”), oraz zakotwiczenie $ dopasuje się także do miejsca w stringu przed znakiem nowej linii.

Podane powyżej modyfikatory należy podać albo jako drugi parametr do konstruktora obiektu RegExp (jeżeli stosujesz pierwszą podaną powyżej składnię), albo po znaku slash (jeżeli stosujesz tą drugą). Można od razu podać więcej niż jeden – kolejność nie ma znaczenia:

var rx1 = new RegExp('^[0-9]+[a-z]+$', 'gi');
var rx2 = /^[0-9]+[a-z]+$/gi;

Porównywanie ze wzorcem

Jednym z częstych zastosowań wyrażeń regularnych jest sprawdzanie czy podany ciąg znaków (np. wpisany przez użytkownika do formularza) zgadza się z określonym wzorcem. Do wykonania takiego sprawdzenia najprościej jest użyć metody test(), do której przekazuje się testowany string. Przykładowo w celu sprawdzenia poprawności kodu pocztowego można użyć poniższego skryptu:

var kod = '12-345';
var rx = /^\d\d-\d\d\d$/;
if (rx.test(kod))
{
    alert('Kod jest poprawny');
}
else
{
    alert('Kod jest niepoprawny');
}

Wyszukiwanie podciągów

Drugim popularnym zastosowaniem wyrażeń regularnych, oprócz porównywania ze wzorcem, jest wyszukiwanie podciągów i ich pobieranie w celu dalszego przetwarzania. W tym celu można użyć metody exec() obiektu RegExp. Metoda ta, podobnie jak omówiona wcześniej metoda test(), przyjmuje jeden parametr – string który będzie przeszukiwany. Zwraca ona natomiast tablicę, która zawiera następujące elementy:

  • pod indeksem 0 znajduje się ciąg znaków dopasowany do całego użytego wyrażenia regularnego;
  • na pozycjach od 1 do końca znajdują się podciągi dopasowane do fragmentów wyrażenia regularnego otoczonych nawiasami okrągłymi;
  • właściwość input zawiera cały sprawdzany ciąg znaków (ten który był przekazany do metody exec());
  • właściwość index zawiera pozycję w sprawdzanym stringu na której udało się dopasować cały podciąg;
  • właściwość lastIndex zawiera pozycję ostatniego dopasowanego znaku podciągu.

Jeżeli nie uda się dopasować wyrażenia regularnego do podanego stringu, metoda exec() zwraca wartość null.

Poniższy przykład pokazuje jak z ciągu znaków wyłuskać kod pocztowy:

var kod = 'abc 12-345 abc';
var rx = /\d\d-\d\d\d/;
var wynik = rx.exec(kod);
if (wynik)
{
    alert(wynik[0]); // 12-345
}
else
{
    alert('Nie znaleziono');
}

Powyższy przykład wykorzystuje jak pobrać podciąg dopasowany do całego wyrażenia regularnego. Można jednak pobrać też podciągi dopasowane do wyrażeń w nawiasach:

var kod = 'abc 12-345 abc';
var rx = /(\d\d)-(\d\d\d)/;
var wynik = rx.exec(kod);
if (wynik)
{
    alert(wynik[0] + ';' + wynik[1] + ';' + wynik[2]); // 12-345;12;345
}
else
{
    alert('Nie znaleziono');
}

Wyszukiwanie globalne

Wcześniej wspomniałem o modyfikatorze g, który włącza wyszukiwanie globalne. Stosując go można wywoływać metodę exec() wielokrotnie, w celu pobierania kolejno dopasowywanych podciągów. Pętlę przerywa się dopiero wtedy gdy zwróci ona wartość null. Można to np. wykorzystać do wyszukania wszystkich grup cyfr w podanym ciągu znaków:

var kod = 'abc 12-345 abc';
var rx = /\d+/g;
while (wynik = rx.exec(kod))
{
    alert(wynik[0]);
}

W powyższym przykładzie funkcja alert() zostanie wywołana dwukrotnie, wyświetlając kolejno liczby 12 i 345.

Obiekt String i wyrażenia regularne

Oprócz omówionych powyżej metod obiektu RegExp istnieje też kilka metod obiektu String, które też pozwalają na użycie wyrażeń regularnych. Pierwszą z nich jest search(), która po wywołaniu zwraca indeks na którym znajduje się dopasowany podciąg, lub -1 jeżeli nie udało się go odnaleźć. Zwrócony indeks można potem użyć w celu np. wyciągnięcia podciągu, albo po prostu sprawdzić czy jest mniejszy od zera lub nie – ta ostatnia metoda może być użyta jako zamiennik wywołania funkcji RegExp.test().

var kod = '12-345';
var rx = /^\d\d-\d\d\d$/;
if (kod.search(rx) >= 0)
{
    alert('Kod jest poprawny');
}
else
{
    alert('Kod jest niepoprawny');
}

Warto też wspomnieć że funkcja String.search() sprawdza czy przekazany parametr jest obiektem RegExp. Jeżeli nie, to utworzy ona taki, przekazując podany parametr do konstruktora. Dlatego możliwe jest też przekazanie jako parametru stringu opisującego wyrażenie regularne:

var kod = '12-345';
if (kod.search('^\d\d-\d\d\d$') >= 0)
{
    alert('Kod jest poprawny');
}
else
{
    alert('Kod jest niepoprawny');
}

Kolejną metodą obiektu String pozwalającą na użycie wyrażeń regularnych jest match(). Funkcja ta, podobnie jak opisywana powyżej search(), może jako parametr przyjąć obiekt RegExp lub string opisujący wyrażenie regularne – w tym drugim przypadku funkcja utworzy sobie obiekt RegExp. Po jej wywołaniu wewnętrznie wywoła ona funkcję RegExp.exec(). Dokładny sposób działania zależy jednak od tego czy wyrażenie regularne używało modyfikatora global, czy też nie. Jeżeli nie, to funkcja String.search() po prostu wywoła funkcję RegExp.exec() i zwróci otrzymany wynik, czyli tablicę ze stringiem dopasowanym do całego wyrażenia regularnego oraz stringami dopasowanymi do fragmentów wyważenia regularnego w nawiasach okrągłych.

Jeżeli natomiast modyfikator global był użyty, wtedy funkcja String.match() będzie wywołać funkcję RegExp.exec() w pętli, dopóki będzie ona zwracać wartość różną od null (podobnie jak w jednym z powyższych przykładów, demonstrujących ten sposób użycia RegExp.exec()). Rezultatem działania tak wywołanej funkcji String.match() będzie tablica zawierająca kolejno znalezione dopasowania wyrażenia regularnego. Jeżeli wyrażenie regularne zawierało fragmenty w nawiasach okrągłych, nie będą one zwrócone. Z tego też powodu funkcja ta jest użyteczna do wyszukiwania wszystkich podciągów opisanych całym wyrażeniem regularnym – dzięki niej można zaoszczędzić trochę pisania. Jeżeli jednak potrzebujesz też użyć w wyrażeniu regularnym nawiasów okrągłych aby pobrać dodatkowe podciągi, wtedy musisz bezpośrednio użyć RegExp.exec().

Wyrażenia regularne i zamiana podciągów

Obiekt String posiada także metodę replace, którą można użyć do zamiany podanych ciągów znaków na inne w całym ciągu znaków. Metoda ta posiada dwa parametry, opisujące to co chcemy zamienić, i to na co ma być to zamienione. Pierwszym z nich może być zwykły string, lub wyrażenie regularne. Drugim zaś może być string albo funkcja. Ponieważ najprostszy przypadek, czyli prostą zamianę stringu na inny, opisałem już w lekcji Stringi, tutaj tylko wstawiam prosty przykład w ramach przypomnienia:

var str = 'Ala ma kota';
str = str.replace('Ala', 'Ola');
alert(str); // Ola ma kota

Funkcja ta pozwala też użyć w drugim parametrze specjalnych kodów rozpoczynających się od znaku dolara (“$”). Kody te zostaną zamienione na odpowiednie fragmenty stringu który był przeszukiwany:

  • $$ – zostanie zamienione na znak dolara;
  • $& – zostanie zamienione na dopasowany ciąg znaków;
  • $` (odwrotny apostrof) – zostanie zamienione na ciąg znaków poprzedzający dopasowany podciąg;
  • $' (zwykły apostrof) – zostanie zamienione na ciąg znaków znajdujący się po dopasowanym podciągu;
  • $n – zostanie zamienione na podciąg dopasowany do n-tego fragmentu wyrażenia regularnego ujętego w nawiasy okrągłe;
  • $nn – zostanie zamienione na podciąg dopasowany do nn-tego fragmentu wyrażenia regularnego ujętego w nawiasy okrągłe.

Jak widać, dwa ostatnie kody dają spore możliwości gdy funkcja zostanie wywołana z wyrażeniem regularnym jako pierwszym parametrem. Można np. zamienić format dat w tekście, z DD-MM-RRRR na RRRR-MM-DD:

var str = '11-06-2009';
var rx = /(\d\d)-(\d\d)-(\d\d\d\d)/;
str = str.replace(rx, '$3-$2-$1');
alert(str); // 2009-06-11

Jak wspomniałem wcześniej, możliwe jest też przekazanie funkcji jako drugiego parametru. Funkcja ta zostanie wywołana gdy pierwszy parametr zostanie dopasowany do jakiegoś podciągu, opisanego pierwszym parametrem metody replace(). Jako parametry do tej funkcji przekazane zostaną: cały dopasowany podciąg, podciągi dopasowane do fragmentów wyrażenia regularnego w nawiasach kwadratowych, pozycja w ciągu wejściowym na której nastąpiło dopasowanie oraz cały ten ciąg wejściowy. Funkcja ta powinna zwracać string, na który zostanie zamieniony dopasowany podciąg. Dla przykładu można to wykorzystać do zamiany pierwszej litery na dużą w wyrazach pisanych małymi literami:

function z_duzej_litery(str, pierwsza, litery, pozycja, ciag)
{
    return pierwsza.toUpperCase() + litery;
}
 
var str = 'ala oLA kasia';
var rx = /([a-z])([a-z]+)/g;
str = str.replace(rx, z_duzej_litery);
alert(str); // Ala oLA Kasia

Warto pamiętać że domyślnie metoda replace() zamienia tylko pierwszy znaleziony podciąg. Aby znaleźć i zamienić wszystkie podciągi, należy użyć modyfikatora global, tak jak w powyższym przykładzie.

Podział na podciągi

Ostatnią metodą obiektu String która współpracuje z wyrażeniami regularnymi – jest split(). Metoda ta służy do podziału ciągu znaków na podciągi, w miejscach gdzie znajduje się podany separator (przekazywany jako pierwszy parametr). Separator ten może być stringiem lub wyrażeniem regularnym. Jeżeli jest to string, to ciąg znaków jest dzielony w miejscach gdzie zostanie on odnaleziony – w ten sposób można np. podzielić na części serię liczb rozdzielonych średnikami.

Drugą metodą zaś jest przekazanie wyrażenia regularnego jako separatora. W tym przypadku ciąg znaków zostanie podzielony w miejscach gdzie wyrażenie regularne zostanie dopasowane. Można to wykorzystać np. do podziału tekstu na części w miejscach gdzie znajdują się znaczniki HTML:

var str = '<p>aaa<b>bbb</b>ccc</p>';
var rx = /<[^>]+>/;
tab = str.split(rx);
// [ '', 'aaa', 'bbb', 'ccc', '' ]

W wyniku użycia powyższego kodu dostaniemy tablicę zawierającą wszystkie teksty znajdujące się poza znacznikami HTML. Zwróć uwagę że pierwszym i ostatnim elementem tablicy są puste ciągi znaków – jeżeli są one niepożądane, musisz napisać kawałek kodu JS który je usunie.

Wyrażenie regularne przekazane jako separator do metody split() może też zawierać fragmenty w nawiasach okrągłych. W takim przypadku zostaną one także zwrócone w tablicy wynikowej. Stosując je można przerobić powyższy przykład, tak aby w tablicy wynikowej znalazły się także znaczniki HTML:

var str = '<p>aaa<b>bbb</b>ccc</p>';
var rx = /(<[^>]+>)/;
tab = str.split(rx);
// [ '', '<p>', 'aaa', '<b>', 'bbb', '</b>', 'ccc', '</p>', '' ]