Jak použít v regulárních výrazech přepínače?
21.12.2005 06:00 | Jiří Václavík | přečteno 27851×
Přepínače (volby) mění chování regulárního výrazu. Uvádějí se na konec regulárního výrazu (tedy za poslední lomítko, je-li uvozovacím znakem). Jejich počet není omezen. Lze je definovat globálně nebo, jak poznáme v příštím díle, lokálně.
Kompletní seznam přepínačů pro hledání a nahrazování je v tabulce:
Přepínač | Význam |
e | vyhodnocuje náhradu jako výraz (jen u nahrazování) |
g | pamatuje si pozici, na které skončilo poslední vyhledávání |
i | nezáleží na velikosti písmen |
m | metaznaky ^ resp. $ jsou na začátku resp. konci všech řádků |
o | překládá vzor jen jednou |
s | množina značící se tečkou zahrnuje i znak nového řádku |
x | speciální syntaxe regulárních výrazů s komentáři |
S některými jsme se již setkali, s některými zatím ne. Nyní jejich použití komplexně shrneme.
print "MATCHED" if "Perl" =~ /perl/i; #vyhovuje
Regulární výraz vrátí true. V řetězci se sice "perl" nikde nevyskytuje, ale protože s přepínačem i se nerozlišuje velikost písmen, vyhoví i "Perl".
Uvedením přepínače g si Perl zapamatuje, na které pozici byl nalezen výskyt a příště pokračuje od ní. To se dá využít v testu cyklu. Používá se k vyhledávání nebo nahrazování všech výskytů (implicitně je hledán jen 1. výskyt).
$retezec = "www.linuxsoft.cz";
while ($retezec =~ /linux/g){
$i++;
}
print "Počet výskytů slova linux v řetězci je $i\n";
Existuje i speciální varianta přepínače g a to gc. Jejím použitím zabráníte resetu hodnoty v případě nezdaru při porovnávání.
V seznamovém kontextu lze přepínač g užít, chceme-li získat seznam všech zapamatovaných řetězců nebo řetězců, které vyhověli vzoru.
@slova = ($retezec =~ /\w+/g);
Přepínač x aktivuje speciální zápis regulárních výrazů. Čitelnost výrazu přitom rapidně roste. Jsou totiž ignorovány mezery a znaky nového řádku v regulárních výrazech. To mimo jiné umožňuje další příjemnou věc, kterou je možnost využití komentářů. Pozor si dejte jen na lomítko, popř. jiný zvolený uvozovací znak v komentáři.
Připomeňme si vzor, kterému vyhoví dvanáctihodinový čas:
/^((0\d)|(1[0-2]))[:\.][0-5]\d[:\.][0-5]\d[ ]?[pa]m$/i;
Na 1. pohled asi těžko poznáte, co by měl takový výraz vyjadřovat. S přepínačem x to bude za pár sekund jasné:
$cas = "12:22:11 am";
print "MATCHED\n" if $cas =~ /
#regulární výraz pro formát dvanáctihodinového času
^
((0\d)|(1[0-2])) #HODINA - číslo mezi 00 a 12
[:\.] #oddělovač hodin a minut
[0-5]\d #MINUTA - číslo mezi 00 a 59
[:\.] #oddělovač minut a sekund
[0-5]\d #SEKUNDA - číslo mezi 00 a 59
[ ]? #nepovinná mezera
[pa]m #určení doby - dopoledne nebo odpoledne; přepínač i zajišťuje, že nezáleží na velikosti písmen
$
/xi;
Toto, jak se dozvíte příštím díle, není jediný způsob, jak vkládat do regulárních výrazů komentáře.
Zejména pro urychlení programu se používá přepínač o. Regulární výraz v nějakém cyklu s přepínačem o je přeložen vždy pouze jednou a tento překlad je pak použit v každé iteraci. Tedy bez ohledu na hodnoty proměnných, které, jak víme, lze do vzorů také zakomponovat. Proměnné, uvedené v regulárním výrazu, se mohou měnit, a pak se tedy mění i samotný regulární výraz. Přepínač o tomu z výše uvedeného důvodu zamezuje. Každou iteraci je použit regulární výraz, který vznikl kompilací v první iteraci.
S přepínačem o souvisí jiná věc. Existuje možnost předkompilace - použití konstrukce qr//. Pokud takový regulární výraz přiřadíte do proměnné, lze ji používat místo onoho regulárního výrazu.
$reg = qr/\d\d\d/;
print "MATCHED" if "12" =~ $reg; #nevyhovuje
print "MATCHED" if "123" =~ $reg; #vyhovuje
print "MATCHED" if "77a" =~ $reg; #nevyhovuje
print "MATCHED" if "7744" =~ $reg; #vyhovuje
Je též možné takový regulární výraz v proměnné zařadit do jiného regulárního výrazu.
print "MATCHED" if "7744" =~ /^$reg$/; #nevyhovuje
print "MATCHED" if "7744" =~ /^\d$reg$/; #vyhovuje
Zkusíte-li proměnnou, ve které je regulární výraz uložen, vytisknout, bude vypadat výstup v našem případě takto: (?-xism:\d\d\d).
S touto vlastností můžete plodit divy. U nahrazování jsme psali náhradu jako text. Uvedení přepínače e umožní napsat náhradu jako výraz (má to mnoho společného s eval). To by nebylo nic objevného, kdyby v ní nešlo používat zapamatované proměnné. Právě v tom tkví kouzlo.
Uvedeme si 2 příklady. Přepínač e se dobře vysvětluje pomocí funkce reverse. Převrátíme pořadí písmen ve všech slovech řetězce:
$retezec = "prisel jsem - videl jsem - zvitezil jsem";
$retezec =~ s/(\w+)/reverse $1/ge;
print $retezec; lesirp mesj - lediv mesj - lizetivz mesj
V proměnné $1 máme uložena postupně všechna (je použit také přepínač g) slova a na každé je aplikována funkce reverse.
Pojďme dál a zkusme něco složitějšího. V řetězci, který budeme zpracovávat, se vyskytují ceny v amerických dolarech. Upravíme tento řetězec regulárním výrazem tak, abychom dolary nahradili českými korunami a to se vším všudy. Musíme tedy přepočítat částku v dolarech na částku v korunách a zaměnit symbol měny. Navíc je nutné ošetřit případné desetinné ceny.
$usd2czk = 24.133; #kurz k dolaru z 19.12.2005
$retezec = "Cena: 20.5\$";
$retezec =~ s/((\d+(.\d+)?)(\$|USD))/$2*$usd2czk.CZK/ge;
print $retezec;
Předpokládali jsme, že symbol měny se píše vždy za hodnotu a mezi hodnotou a symbolem není mezera. Tisknut je řetězec "Cena: 494.7265CZK". Je zde ponecháno zbytečně mnoho desetinných míst. Pomocí funkce sprintf je lze oříznout tak, aby byly vytištěny vždy právě dvě, případně doplněné nulami.
$retezec =~ s/((\d+(.\d+)?)(\$|USD))/sprintf("%.2f", $2*$usd2czk).CZK/ge;
Pokud si zkusíte nahradit vstupující text za nějaký složitější, kde se vyskytuje cena v dolarech vícekrát, jsou nahrazeny všechny. Zajímavé by také mohlo být získávání aktuálního kurzu za běhu.
Přepínač e má ještě jednu zajímavou vlastnost. Lze ho uvést pro 1 regulární výraz vícekrát. Ilustrujme si to na této ukázce.
$xxx = "XXX";
$_ = "***\$xxx***";
s/(\$\w+)/$1/ee;
print;
Počáteční stav s 2 přepínači e:
s/(\$\w+)/$1/ee;
V 1. kroku je jedno e spotřebováno na nahrazení proměnné $1 svým obsahem, tedy řetězcem '$xxx'.
s/(\$\w+)/$xxx/e;
Zbývá nám ještě poslední e, které vyhodnotí výraz $xxx - tedy nahradí proměnnou $xxx jejím obsahem.
s/(\$\w+)/XXX/;
Příště budeme v regulárních výrazech pokračovat a podíváme se na zoubek rozšířeným vzorům.
Online verze článku: http://www.linuxsoft.cz/article.php?id_article=1053