Regulární výrazy a JavaScript – metody test() a exec()

19. července 2005

Metody String objektů používajících regulární výrazy jako svůj argument jsme si ukázali v předchozích dvou článcích. V tomto článku se budeme věnovat metodám RegExp objektů. V závěru se také podíváme na malé srovnání práce s regulárními výrazy v JavaScriptu a PHP.

Metody a vlastnosti objektu RegExp

Pro práci s regulárními výrazy disponuje objekt RegExp těmito metodami:

  • test() – zjistí, zda řetězec či alespoň jeho část odpovídá regulárnímu výrazu
  • exec() – najde a vrátí část řetězce odpovídající regulárnímu výrazu (a případně jednotlivým subvýrazům)

Instance objektu RegExp mají tyto vlastnosti:

  • source – zdrojový text regulárního výrazu
  • global – zda je použit modifikátor g (boolean hodnota)
  • ignoreCase – zda je použit modifikátor i (boolean hodnota)
  • multiline – zda je použit modifikátor m (boolean hodnota)
  • lastIndex – pozice za posledním znakem poslední shody (má smysl při použití modifikátoru g)

Výše uvedené vlastnosti jsou vlastností instance objektu RegExp až od verze JavaScriptu 1.5. Dříve byly přímo vlastností třidy RegExp (což je docela nelogické).

test()

Zapisujeme ve tvaru regexp.test(řetězec), kde regexp je regulární výraz, kterému má odpovídat řezětec. Funkce vrací hodnotu true, pokud v řetězci byla nalezena shoda s regexpem, jinak vrací false.

//test() – příklad 1
var str=“15 USD, 10 EUR, 300 CZK“;
var re1=/(\d+)\s(\w+)/;
var result1=re1.test(str); //vrací: true
alert(„Výsledek: „+result1);

V prvním příkladu není použit modifikátor g a tak se po nalezení shody (15 USD) v dalším hledání možných shod nepokračuje a funkce vrátí true.

Pokud je nastaven modifikátor g, provede se globální vyhledávání. To v praxi znamená, že ve vlastnosti lastIndex se zapamatuje pozice v řetězci, která následuje po posledním znaku předchozí shody. Tuto poněkud krkolomnou větu snadno pochopíme na následujícím příkladu:

//test() – příklad 2
var str=“15 USD, 10 EUR, 300 CZK“;
var re2=/(\d+)\s(\w+)/g;
var result2;
var i=0; //inicializace – počet nalezených shod
while(result2=re2.test(str))
{
   i++; //inkrementace počtu nalezených shod
   alert(„Pocet nalezenych: „+i+“\nDalsi hledani od pozice: „+re2.lastIndex);
};

V tomto příkladu je nastaven modifikátor g. Metoda test() i v tomto případě vrací pouze hodnoty true a false. Rozdíl je ovšem v tom, že vyhledávání nezačíná vždy od prvního znaku řetězce (jako je tomu u neglobálního vyhledávání), ale od pozice určené vlastností regulárního výrazu (v našem příkladu result2) lastIndex. V případě, že byly nalezeny všechny shody, nastaví se vlastnost lastIndex na 0 (nastavení lastIndex na nulu můžeme provést i explicitně, pokud chceme zajistit vyhledávání od začátku řetězce, i když ještě nebyly nalezeny všechny shody).

Chceme-li tedy zjistit pomocí metody test() počet shod regulárního výrazu s řetězcem, můžeme provést volání v cyklu a při každém vrácení hodnoty true inkrementovat nějaké vlastní počítadlo (v našem příkladu proměnná i). V našem cyklu (kromě inkrementace tohoto počítadla a zobrazení jeho hodnoty) navíc zobrazujeme aktuální hodnotu vlastnosti lastIndex.

exec()

Zapisujeme ve tvaru regexp.exec(řetězec), kde regexp je regulární výraz, kterému má odpovídat řetězec. V případě shody vrací metoda pole, které obsahuje pod indexem 0 řetězec odpovídající celému regexpu a v dalších prvcích pole shody s jednotlivými subvýrazy regulárního výrazu (tedy vrací stejné pole jako metoda match() objektu String při neglobálním vyhledávání).

Kromě toho má navrácené pole dvě objektové vlastnosti – index a input. Vlastnost index obsahuje informaci o pozici, na níž začíná shoda. Vlastnost input obsahuje řetězec, v němž je vyhledáváno. V případě nenalezení shody vrací metoda null.

Rozdíl mezi metodou exec() a dříve zmíněnou metodou match() je v tom, že exec() vrací pouze první shodu i v tom případě, že je použito globální vyhledávání (použit modifikátor g). Chování metody exec() je na druhou stranu podobné výše zmíněné metodě test(). Podobnost tkví ve využívání vlastnosti lastIndex (při globálním vyhledávání) pro uchování informace o pozici, od níž se má pokračovat v prohledávání řetězce. Vše nejlépe uvidíme na příkladu:

//exec() – příklad 1
var str=“15 USD, 10 EUR, 300 CZK“;
var re1=/(\d+)\s(\w+)/g;
var i=0; //inicializace – počet nalezených shod
var subvyrazy; //řetězec pro zapamatování subvýrazů
while(result1=re1.exec(str))
{
   i++; //inkrementace počtu nalezených shod
   subvyrazy=““;
   for(var j=1;result1[j];j++)
      subvyrazy=subvyrazy+“\n[„+j+“]: „+result1[j];
   alert(„Vyhledava se v retezci: „+result1.input
   +“\nNalezena shoda:“+result1[0]
   +“\nShoda zacina na pozici: „+result1.index
   +“\nPocet doposud nalezenych: „+i
   +“\nDalsi hledani od pozice: „+re1.lastIndex
   +“\n\nShody se subvyrazy:“+subvyrazy);
};

V cyklu while voláme opakovaně re1.exec(str) (podobně, jako jsme volali ve výše uvedeném příkladu metodu test()). Tento cyklus se zastaví až v okamžiku, kdy nebude nalezena další shoda a volání re1.exec(str) vrátí null (což se zkonvertuje na false). Cyklus for pak slouží pouze k procházení shod s jednotlivými subvýrazy (jak víme, řetězec odpovídající prvnímu subvýrazu bude v prvku pole s indexem 1, proto proměnnou j inicializujeme na hodnotu 1) a jejich zapsání do řetězce subvyrazy (kvůli následnému zobrazení pomocí alert()).

Funkce alert() tak pro každou shodu zobrazí celý řetězec v němž se vyhledává (result1.input), shodu – tedy část řetězce odpovídající celému regulárnímu výrazu (result1[0]), pozici v řetězci, kde začíná shoda (result1.index), „naše interní“ počítadlo shod (i), pozici v řetězci, od níž začne další vyhledávání (re1.lastIndex), a nakonec naši proměnnou subvyrazy.

Závěrem ještě malá poznámka. Výše uvedené příklady (příklad 2 u metody test() a příklad 1 u metody exec()) budou korektně fungovat pouze za předpokladu použití modifikátoru g. V případě jeho nepoužití je vždy vyhledáváno od začátku řetězce. Podmínka while je tedy splněna vždy, pokud je v řetězci alespoň jedna shoda. Cyklus se tak stane nekonečnou smyčkou. Řešení nezávislé na (ne)použití modifikátoru g si ukážeme jindy.

Co JavaScript nepodporuje a jiné odlišnosti

Ačkoliv má JavaScript ve verzi 1.5 docela slušnou podporu Perl-compatible regulárních výrazů, na některé věci si musíme nechat zajít chuť. V čem se tedy liší funkčnost regulárních výrazů v JavaScriptu oproti PHP?

Co JavaScript nepodporuje

  • Vždy (bez ohledu na modifikátor multiple-lines) platné hranice pro začátek a konec řetězce (v PHP \A a \Z).
  • Modifikátory „single-line“ (změna chování metaznaku tečka).
  • Modifikátor „extended“ (komentáře v regulárním výrazu a ignorování bílých znaků).
  • Modifikátor „ungreedy“ (globální zapnutí líného chování všech nenasytných kvantifikátorů).
  • Komentáře.
  • Pojmenované subvýrazy (možnost odkazovat se zvoleným názvem místo číslem).
  • Tvrzení o předcházejícím.
  • Podmíněné subvýrazy.

Co má JavaScript navíc

  • Reference na text předcházející textu, který vytváří shodu s regulárním výrazem ($`).
  • Reference na text následující za textem, který vytváří shodu s regulárním výrazem ($').

Odlišnosti ve funkcích pro práci s regulárními výrazy

  • Výsledku, který nám v PHP zajistí funkce preg_match_all(), můžeme v JavaScriptu dosáhnout použitím RegExp metody exec() v cyklu (viz příklad výše).
  • Funkčnost, kterou v PHP zajišťuje funkce preg_replace_callback(), v JavaScriptu obsahuje přímo String metoda replace() (viz čtvrtý příklad uvedený u popisu metody replace() v předchozím článku).

Ke stažení

Veškeré zde uvedené příklady si můžete stáhnout a otestovat přímo ve svých prohlížečích, samozřejmě jen v případě, že podporují JavaScript a regulární výrazy.

Předchozí článek lesniroh.cz
Štítky: Články

Mohlo by vás také zajímat

Nejnovější

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *