Deprecated: Function set_magic_quotes_runtime() is deprecated in /mnt/data/accounts/w/webmark/data/www/blog/textpattern/lib/txplib_db.php on line 14
Perl-compatible regulární výrazy v PHP (6) - speciální typy uzávorkování :: Blog v pavučině
Blog v pavučině je mrtev. Ať žije nový blog Všeho s Mírou
Skočit na obsah Skočit na menu Skočit na vyhledávání

Perl-compatible regulární výrazy v PHP (6) - speciální typy uzávorkování

Kulaté závorky jsme doposud používali pro ohraničení subvýrazů, a to buď proto, že jsme chtěli subvýraz doplnit kvantifikátorem, nebo proto, že jsme se na řetězec odpovídající subvýrazu chtěli odkazovat pomocí zpětné reference. Kulaté závorky se ale v Perl-compatible regulárních výrazech používají také k sestavení speciálních konstrukcí, které ovlivňují chování regulárního výrazu.

Mezi tyto konstrukce patří:

  • uzávorkování netvořící zpětné reference (non-capturing parentheses)
  • pojmenované subvýrazy (named subpatterns), respektive pojmenované zpětné reference
  • komentáře (další možnost jejich zápisu)
  • modifikátory (další možnost jejich zápisu)
  • "look ahead assetions" a "look behind assertions" (což můžeme v tomto kontextu přeložit asi jako "tvrzení o následujícím" a "tvrzení o předcházejícím")
  • podmíněné subvýrazy

Pokud použijeme například regulární výraz ^a(bc)+d$, budeme moci využít zpětnou referenci na první (jediný) subvýraz. Jestliže však víme, že zpětnou referenci nebudeme potřebovat, ale přesto potřebujeme určit opakování určité sekvence znaků (v našem případě bc následované kvantifikátorem +), pak můžeme použít takzvané uzávorkování netvořící zpětné reference (non-capturing parentheses).

Pokud použijeme regulární výraz ^a(?:bc)+d$, sekvence ?: za levou kulatou závorkou zajistí, že na řetězec (bc) odpovídající výrazu v těchto závorkách se nebude možno odkazovat pomocí zpětné reference. Použití "uzávorkování netvořícího zpětné reference" má dvě výhody. Jednak si počítač při zpracování regulárního výrazu ušetří práci (respektive místo v paměti), když si nebude muset pamatovat zpětné reference, jednak se tímto způsobem může zpřehlednit práce se zpětnými referencemi v složitém regulárním výrazu.

Ve složitějším regulárním výrazu můžeme mít mnoho subvýrazů, které tvoří zpětnou referenci. Pak nemusí být snadné se v očíslování jednotlivých subvýrazů vyznat. Krom toho se může stát, že na začátku regulárního doplníte později další subvýraz a všechny subvýrazy (vpravo od něj) se tím přečíslují. Vhodné proto je jednotlivé subvýrazy pojmenovat vlastním označením (můžeme použít alfanumerické znaky a podtržítko).

Takzvané pojmenované subvýrazy (named subpatterns) můžeme využít při použití funkce preg_match() ve tvaru preg_match($re,$str,$matched), kde $matched je pole řetězců, které odpovídají jednotlivým subvýrazům. Toto pole je (jak jsme si řekli již dříve) standardně indexováno čísly jednotlivých subvýrazů. Pokud však některé subvýrazy pojmenujeme, bude pole obsahovat i prvky s indexy odpovídajícími těmto pojmenováním. Pro pojmenování subvýrazu se používá konstrukce (?Pvýraz).

Podívejme se na modelový příklad, v němž máme za úkol otestovat, zda zadaný řetězec (datum) odpovídá formátu RRRR-MM-DD (přičemž první dvojčíslí roku smí být pouze 19 nebo 20), a pokud odpovídá, zobrazit jednotlivé části data (den, měsíc, rok).

$re="/^(?P(?:19|20)[0-9]{2})-(?P[0-9]{2})-(?P[0-9]{2})$/";
$str="2005-07-12";
$result=preg_match($re,$str,$matched);
if($result) //zadaný řetězec regulárnímu výrazu odpovídá
{
echo "Den: ".$matched["den"]."\n";
echo "Měsíc: ".$matched["mesic"]."\n";
echo "Rok: ".$matched["rok"]."\n";
}
else //zadaný řetězec regulárnímu výrazu neodpovídá
{
echo "Špatný formát data.";
};

Rozeberme si nejdříve samotný regulární výraz. Tento výraz je složen ze tří subvýrazů (pro rok, měsíc a den) tvořících zpětné reference, které jsou odděleny obyčejným znakem pomlčky. První subvýraz (?P(?:19|20)[0-9]{2}) je složen z pojmenování subvýrazu ?P, dále z uzávorkování netvořícího zpětnou referenci (?:19|20) a nakonec ze skupiny znaků [0-9] následované kvantifikátorem {2} (poslední dvojčíslí roku). Druhý subvýraz (?P[0-9]{2}) se skládá opět z pojmenování subvýrazu ?P, za nímž následuje samotný výraz [0-9]{2} popisující dvojciferné číslo. Třetí subvýraz je s druhým až na pojmenování shodný.

Výše uvedený regulární výraz není zcela dokonalý, záměrně jsem jej pro účely demonstrace probíraných principů zjednodušil. Regulárnímu výrazu [0-9]{2} totiž například odpovídá i sekvence znaků 00. Krom toho regulární výraz má popisovat měsíc (respektive den v měsíci), takže například číslo 56 by také nemělo být považováno za správný vstup. Dokonalejším řešením jistě bude výraz ^(?P(?:19|20)[0-9]{2})-(?P0[1-9]|1[0-2])-(?P0[1-9]|[12][0-9]|3[01])$. Zde je pro popis měsíce použito 0[1-9]|1[0-2] (místo původního [0-9]{2}) a pro popis dne v měsíci 0[1-9]|[12][0-9]|3[01] (místo původního [0-9]{2}).

Vraťme se nyní ještě na skok k poli $matched, které obsahuje řetězce odpovídající jednotlivým subvýrazům, a podívejme se, co ve skutečnosti v našem příkladu obsahuje. K zobrazení obsahu pole jsme použili příkaz var_dump($matched).

array(7) {
[0]=>
string(10) "2004-07-12"
["rok"]=>
string(4) "2004"
[1]=>
string(4) "2004"
["mesic"]=>
string(2) "07"
[2]=>
string(2) "07"
["den"]=>
string(2) "12"
[3]=>
string(2) "12"
}

Jak vidíme, pole nemá čtyři prvky, jak bychom mohli očekávat, ale sedm prvků. Pokud totiž použijeme pojmenované subvýrazy, hodnota odpovídající subvýrazu se do pole uloží dvakrát. Jednou s indexem odpovídajícím číslu subvýrazu, podruhé s indexem (klíčem) odpovídajícím pojmenování subvýrazu.

Pokud máme složitější regulární výraz, můžeme do něj (i na několik míst) vložit vysvětlující komentáře. Jedna možnost, jak přidat komentáře, byla popsána v předchozím článku (použití modifikátoru "extended"). Druhou možností je vložení speciální konstrukce ve tvaru (?#komentář) do regulárního výrazu. Regulárnímu výrazu ^a(?#můj komentář)b$ tak bude vyhovovat právě a pouze řetězec ab, protože za komentář se považují všechny znaky mezi (?# a ).

Tento článek byl původně publikován na serveru Interval.cz, kde naleznete originální verzi článku.