Navigace

Hlavní menu

 

Perl-compatible regulární výrazy v PHP - modifikátory a líné kvantifikátory

V předchozím článku jsme nakousli téma modifikátorů, a protože modifikátory jsou příliš velkým soustem pro jeden článek, budeme v tomto tématu pokračovat a ukážeme si další čtyři modifikátory.

"Single line"

Jak jsem psal již v prvním článku, metaznaku . (tečka) odpovídají všechny znaky kromě \n. Pokud je však použit modifikátor "single line" (který má ovšem v PHP interní označení "PCRE_DOTALL"), metaznak . bude odpovídat i znaku konce řádku \n. Modifikátor "single line" má zkratku "s".

"Extended" aneb komentáře v regulárních výrazech

Pokud použijeme modifikátor "extended" (který má zkratku "x"), způsobí to dvě změny ve vyhodnocování regulárního výrazu:

  1. Takzvané "bílé znaky" v regulárním výrazu budou ignorovány, s výjimkou bílých znaků zapsaných v rámci skupiny znaků a s výjimkou případu, kdy bílému znaku bude předcházet zpětné lomítko \.
  2. Část regulárního výrazu vpravo od znaku # až po znak konce řádku \n (včetně) bude ignorována. To umožňuje vkládat ve složitých regulárních výrazech komentáře za znak #.

Použití předvedu opět na ukázce:

$re="/^ ah\n\toj$#můj vlastní komentář/x";
$str="ahoj";
preg_match($re,$str); //vrátí int(1), tedy shodu

V uvedeném regulárním výrazu jsou obsaženy jak bílé znaky   (mezera), \n, \t, tak i text komentáře (#můj vlastní komentář), který bude zcela ignorován.

"Evaluate" - speciální modifikátor při nahrazování

Modifikátor se zkratkou "e" (odvozeno od "evaluate", anglicky "vyhodnotit") může být použit pouze v regulárních výrazech, které jsou argumentem funkce preg_replace() (respektive jinde je jeho použití ignorováno). Použijeme-li tento modifikátor, řetězec obsahující "text náhrady" (v našich příkladech jsme pro něj používali proměnnou $replacement) bude vyhodnocen jako PHP kód. Použití si ukážeme na příkladu, kdy bude naším cílem převést všechny HTML tagy na malá písmena (pozor, pouze tagy, nikoli atributy).

$re="/(<\/?)([a-z]+)([^>]*>)/ei";
$replacement="'\\1'.strtolower('\\2').'\\3'";
$str="<H1>Nadpis</H1>
<P>Text1</P>
<P>Text1</P>";
$result=preg_replace($re,$replacement,$str);

Výsledkem (obsahem proměnné $result) pak je HTML kód:

<h1>Nadpis</h1>
<p>Text1</p>
<p>Text1</p>

Všimněte si, že kromě modifikátoru "e" jsme použili i modifikátor "i" (case insensitive). Jak vidíte, modifikátory lze tedy kombinovat. Stačí je zapsat za sebou za koncový oddělovač / (na pořadí zápisu modifikátorů přitom nezáleží).

Regulárnímu výrazu v proměnné $re odpovídají otevírací i uzavírací (případně nepárové) HTML tagy (s i bez atributů). Výraz je složen ze tří subvýrazů. Prvnímu subvýrazu odpovídá < (otvírací tag) nebo </ (uzavírací tag). Druhému subvýrazu odpovídá sekvence znaků a-z (respektive A-Z, protože je použit modifikátor "i"), která představuje tag samotný. Třetímu subvýrazu odpovídá sekvence téměř libovolných znaků (vše kromě znaku >) následovaná znakem >.

Jak jste si asi všimli, tento příklad není nepodobný příkladu číslo 3 z předchozího článku. V tomto případě však zpracujeme řetězec představovaný zpětnou referencí na druhý subvýraz (\\2) pomocí PHP funkce strtolower(), která převádí řetězec na malá písmena. Protože je nyní (kvůli modifikátoru "e") řetězec v proměnné $replacement vyhodnocován jako PHP kód, musí být zpětné reference \\1 a \\3 uzavřeny (jakožto řetězce) do jednoduchých uvozovek a spojeny s dalšími řetězci (respektive řetězcem vracejícím volání funkce strtolower('\\2')) pomocí běžného PHP operátoru pro spojování řetězců (. - tečka).

Kvantifikátory podruhé - "lenost" kontra "nenasytnost"

Již v prvním článku jsme si uvedli seznam všech kvantifikátorů a doposud jsme předpokládali, že se bude kvantifikátor snažit "pozřít" co možná nejdelší řetězec. Tedy například, když porovnáme řetězec abbb s regulárním výrazem ^a(b+), pak prvnímu subvýrazu bude odpovídat řetězec bbb. Daný regulární výraz by se samozřejmě "spokojil" (přesněji řečeno "by odpovídal") i s řetězcem ab, má-li však k dispozici delší řetězec, použije nejdelší možnou variantu, která ještě vede ke shodě. Proto říkáme, že kvantifikátory jsou standardně "nenasytné" (greedy).

Pomocí "ungreedy" modifikátoru (který má zkratku "U" a v PHP interní označení "PCRE_UNGREEDY") je možno přepnout chování kvantifikátorů z "nenasytného" na "líné" (lazy). Pokud se budou kvantifikátory chovat líně, bude se kvantifikátor snažit "pozřít" minimální počet znaků, které jsou třeba, aby řetězec odpovídal regulárnímu výrazu. Pokud použijeme (v "ungreedy" módu) opět řetězec abbb a regulární výraz ^a(b+), prvnímu subvýrazu bude odpovídat jednoznakový řetězec b. Zbytek řetězce bude ignorován (nepřehlédněte, že je použito pouze ukotvení za začátku pomocí ^ - vpravo od prvního výskytu b až do konce řetězce tak může být cokoli).

Modifikátor "U" zapne "líné" chování kvantifikátorů globálně pro celý výraz. Je však také možné, aby "zlenivěly" jen vybrané kvantifikátory v řetězci. Každý konkrétní kvantifikátor, který má "zlenivět", musí být bezprostředně následován otazníkem (?).

Místo kvantifikátorů ?, *, +, {m,n}, {m,} (kde m je minimální počet výskytů a n je maximální počet výskytů - více viz první článek), můžeme použít jejich líné varianty ??, *?, +?, {m,n}?, {m,}?.

Následující tabulka názorně ukazuje rozdíly v chování "nenasytných" a "líných" kvantifikátorů, respektive jaká část z modelového řetězce bbbbbb bude odpovídat (vytvářet shodu) jednotlivým regulárním výrazům.

Regulární výrazShoda
b?b
b??(prázdný řetězec)
b*bbbbbb
b*?(prázdný řetězec)
b+bbbbbb
b+?b
b{2, 4}bbbb
b{2, 4}?bb
b{3,}bbbbbb
b{3,}?bbb
b{5}bbbbb

Doplňme ještě, že pokud je pomocí modifikátoru "U" zapnuta globální "lenost", přidáním otazníku za konkrétní kvantifikátor se jeho (lokální) chování přepne na "nenasytné".

Pecka, Miroslav (22. 3. 2005)

Perl-compatible regulární výrazy v PHP

Regulární výrazy jsou mocným nástrojem, který může výrazně zjednodušit a zpřehlednit kód programu. Nejčastěji je můžeme použít při kontrole či "parsování" vstupních dat. Tato série článků dosud nebyla ukončena!