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 (9) - další užitečné funkce :: 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 (9) - další užitečné funkce

Ve všech předcházejících článcích jsme si při předvádění jednotlivých regulárních výrazů vystačili pouze se dvěma funkcemi. V tomto článku se proto seznámíme s několika dalšími užitečnými funkcemi pro práci s Perl-compatible regulárními výrazy v PHP. Vzhledem k tomu, že vše, co se týče konstrukce regulárních výrazů samotných, bylo již probráno, budu se při popisování jednotlivých příkladů věnovat hlavně funkcím samým.

Funkce preg_match_all($re,$str,$matched) funguje velmi podobně jako funkce preg_match($re,$str,$matched) (viz subvýrazy a zpětná reference) jen s tím rozdílem, že najde v řetězci $str všechny řetězce, které odpovídají regulárnímu výrazu $re (zatímco preg_match($re,$str,$matched) najde jen první výskyt). Funkce preg_match_all() vrací počet řetězců, které odpovídají celému regulárnímu výrazu.

Příklad demonstrující rozdílné chování funkcí:

$re="/(CZK|EUR|USD) (\d+)/";
$str="CZK 1200 nebo EUR 40 nebo USD 50";
$result1=preg_match($re,$str,$matched1); //vrátí int(1)
$result2=preg_match_all($re,$str,$matched2); //vrátí int(3)

Pole $matched1 pak bude (var_dump($matched1)):

array(3) {
[0]=>
string(8) "CZK 1200"
[1]=>
string(3) "CZK"
[2]=>
string(4) "1200"
}

Pole $matched2 pak bude (var_dump($matched2)):

array(3) {
[0]=>
array(3) {
[0]=>
string(8) "CZK 1200"
[1]=>
string(6) "EUR 40"
[2]=>
string(6) "USD 50"
}
[1]=>
array(3) {
[0]=>
string(3) "CZK"
[1]=>
string(3) "EUR"
[2]=>
string(3) "USD"
}
[2]=>
array(3) {
[0]=>
string(4) "1200"
[1]=>
string(2) "40"
[2]=>
string(2) "50"
}
}

Index 0 v obou polích ($matched1 a $matched2) označuje, že se jedná o řetězec (respektive řetězce) odpovídající celému regulárnímu výrazu. Řetězce odpovídající jednotlivým subvýrazům jsou indexovány čísly jednotlivých subvýrazů (v našem případě 1, 2). V poli $matched2 je každý prvek ($matched2[0], $matched2[1], $matched2[2]) sám o sobě polem. Indexy v těchto polích odpovídají (třem) jednotlivým nalezeným řetězcům.

Pokud se vám nelíbí, že takto vzniklé dvojrozměrné pole $matched2 je indexováno nejdříve podle subvýrazů a až poté podle nalezených řetězců, můžete toto chování změnit pomocí příznaku PREG_SET_ORDER, který se funkci předá jako její čtvrtý argument.

Použijeme-li stejnou hodnotu proměnných $re a $str jako v přikladu výše a zavoláme funkci preg_match_all($re, $str, $matched3, PREG_SET_ORDER), tato funkce vytvoří pole $matched3:

array(3) {
[0]=>
array(3) {
[0]=>
string(8) "CZK 1200"
[1]=>
string(3) "CZK"
[2]=>
string(4) "1200"
}
[1]=>
array(3) {
[0]=>
string(6) "EUR 40"
[1]=>
string(3) "EUR"
[2]=>
string(2) "40"
}
[2]=>
array(3) {
[0]=>
string(6) "USD 50"
[1]=>
string(3) "USD"
[2]=>
string(2) "50"
}
}

Funkce preg_grep($re, $strings) má dva argumenty (parametry). Prvním argumentem je regulární výraz, druhým argumentem je pole řetězců. Funkce vrací pole složené z prvků (původního pole $strings), které odpovídají regulárnímu výrazu $re.

Řekněme, že máme pole obsahující hesla uživatelů a chceme z nich vybrat pouze hesla delší než 5 znaků.

$re="/\w{6,}/";
$strings=array("uu123","jaro","abcdefg","hohoho");
$result=preg_grep($re,$strings);

Výsledné pole $result bude obsahovat požadovaná hesla:

array(2) {
[2]=>
string(7) "abcdefg"
[3]=>
string(6) "hohoho"
}

Pomocí funkce preg_replace() jsme mohli zaměnit řetězec odpovídající regulárnímu výrazu za jiný řetězec (s využitím řetězce "zachyceného" pomocí regulárního výrazu či subvýrazu). S pomocí preg_replace_callback() dokážeme ještě mnohem více. Řetězec odpovídající regulárnímu výrazu můžeme totiž nahradit řetězcem, který získáme jako návratovou hodnotu vlastní (uživatelsky definované) funkce. Tato naše funkce přitom může pracovat s řetězcem, který odpovídá regulárnímu výrazu (či subvýrazu).

Řekněme, že máme k dispozici seznam dvojciferných čísel, která představují roky (respektive letopočty). Naším úkolem je převést letopočty dvojciferné na letopočty čtyřciferné, a to tak, že do roku 29 včetně budou převáděny na letopočty ve tvaru 20XX a od roku 30 (včetně) výše budou převáděny na letopočty ve tvaru 19XX. Asi tušíte, že naše (uživatelsky definovaná) funkce bude mít za úkol právě rozhodnout zda doplnit "19" či "20".

function letopocet($rok)
{
if($rok[0]<30)
{
$prefix="20";}
else
{
$prefix="19";
};
return $prefix.$rok[0];
};

$re="/\d{2}/";
$function_name="letopocet"; //název uživatelské funkce
$str="12, 91, 45, 29, 30, 01";
$result=preg_replace_callback($re,$function_name,$str);

V proměnné $result pak získáme řetězec 2012, 1991, 1945, 2029, 1930, 2001.

Celé to funguje tak, že funkce preg_replace_callback() zavolá uživatelsky definovanou funkci (jejíž název obdrží jako hodnotu svého druhého argumentu) a té předá pole obsahující řetězec, který odpovídá regulárnímu výrazu (případně též řetězce, které odpovídají jednotlivým subvýrazům). Řetězec, který odpovídá celému regulárnímu výrazu, je v poli pod indexem 0. Uživatelská funkce zpracuje nějakým způsobem předaná data a pošle svou návratovou hodnotu zpět funkci preg_replace_callback(). Toto se provede pro každé nahrazení. V okamžiku, kdy jsou všechna nahrazení provedena, může funkce preg_replace_callback() skončit a vrátit svou návratovou hodnotu (tedy celý upravený řetězec).

Funkce preg_split($re, $str), rozdělí řetězec $str na části, které vrátí jako pole řetězců. Jako oddělovač se použije řetězec odpovídající regulárnímu výrazu $re. Opět malý příklad:

$re="/[,; ]+/";
$str="honza@inmail.cz;jarda@jarda.com filip@mudrc.cz ,; tomas@svaty.cz";
$result=preg_split($re,$str);

V našem případě regulárnímu výrazu popisujícímu oddělovač odpovídá (neprázdný) řetězec složený ze znaků čárka, středník a mezera v libovolném pořadí a počtu. A tak pole $result bude obsahovat již jen jednotlivé e-mailové adresy:

array(4) {
[0]=>
string(15) "honza@inmail.cz"
[1]=>
string(15) "jarda@jarda.com"
[2]=>
string(14) "filip@mudrc.cz"
[3]=>
string(14) "tomas@svaty.cz"
}

Tuto funkci můžeme označit jako pomocnou. Nezpracovává totiž určitý řetězec pomocí regulárního výrazu, ale pomůže nám při tvorbě regulárního výrazu. Někdy totiž potřebujeme vytvořit regulární výraz až za běhu našeho skriptu a tak nemůžeme "ručně" ošetřit metaznaky (doplnit před ně \), jestliže chceme potlačit jejich speciální funkčnost.

Za běhu skriptu nám vznikne například řetězec a+b=c, který chceme použít jako regulární výraz. Pokud zavoláme preg_quote("a+b=c"), funkce vrátí řetězec a\+b\=c.

Nesmíme zapomenout, že celý regulární výraz je uzavřen v oddělovačích (delimiters), měli bychom proto také zajistit, aby znaku, který používáme jako oddělovač (v našem případě používáme obyčejné lomítko /) také předcházelo zpětné lomítko \. Protože funkce nemůže sama o sobě vědět, jaký oddělovač používáme, předáme ji tuto informaci v nepovinném druhém argumentu.

Pokud tedy zavoláme například preg_quote("a+b/c=d","/"), funkce vrátí řetězec a\+b\/c\=d. Jak můžete vidět, znak \ je i před znakem =, ačkoli = není metaznak. Funkce totiž doplňuje zpětná lomítka i před znaky, které mohou tvořit speciální konstrukce (například tvrzení o následujícím a předcházejícím). Zpětným lomítkem jsou proto doplněny znaky ., \, +, *, ?, [, ^, ], $, (, ), {, }, =, !, <, >, | a :.

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