Regulární výrazy (5)

Pavel Satrapa 9. 5. 2000

Na pátou část seriálu jsem si pošetřil snad nejsilnější prvek regulárních výrazů - jejich paměť. Regulární výraz si totiž dokáže zapamatovat řetězec, který vyhověl jeho části, a později jej použít. Největší služby tento mechanismus odvede při nahrazování.

Zapamatuj a vzpomeň si

Prostředky pro zapamatování jsou směšně jednoduché. Část, kterou si má regulární výraz podržet v paměti, prostě ohraničíte konstrukcemi \( a ). Jistě si vzpomínáte, že v Perlu nemá nealfanumerický znak předcházený závorkou nikdy speciální význam, takže tam se ke stejnému účelu používají obyčejné závorky. Takových úseků můžete v regulárním výrazu mít hned několik. Dokonce je lze i vzájemně vnořovat.

Když později chcete použít zapamatovaný řetězec, napište \číslo, kde číslo je pořadové číslo zapamatovaného úseku. Pořadová čísla začínají jedničkou a rozhoduje o nich pořadí levé (otevírací) závorky zapamatovávané sekvence.

Příklad:

Podrobíte-li řádek ze souboru /etc/passwd regulárnímu výrazu ^\([^:]*):[^:­]*:\([^:]*), bude \1 obsahovat přihlašovací jméno a \2 jemu odpovídající identifikátor (U­ID).

O dobrodiní paměti jste ochuzeni u programů egrep a awk. Jejich algoritmus pro srovnávání regulárních výrazů nepodporuje zapamatování (a z principiálních důvodů ani podporovat nemůže). V příští části se k této otázce vrátím podrobněji.

widgety

Použití při hledání

Použití zapamatovaných částí při vyhledávání je poměrně vzácné. Řečeno mluvou politiků: společenská potřeba takové služby je celkem malá. Příklady sice existují, nicméně bývají takové školometské. Tak si nějaké zkusíme.

Příklad:

Hezkou a zcela neužitečnou ilustrací zapamatování je hledání palindromů (slov, která se nezmění, když je čtete pozadu). Tak například výrazu

\<\(.\)\(.\).\2\1\>

vyhoví právě a pouze pětiznakový palindrom (např. radar či rotor). První dva znaky slova si zapamatuje, za nimi následuje třetí libovolný znak, čtvrtý musí být stejný jako zapamatovaný druhý a pátý znak se musí shodovat s prvním.

Příklad:

Zkusím něco, co by alespoň vzdáleně připomínalo reálný život. Řekněme, že máte výstupy z jakéhosi algoritmu – na každém řádku sadu čísel oddělených mezerami. Každý řádek zároveň končí správným výsledkem. Pokud algoritmus pracuje správně, poslední dvě čísla na řádku jsou totožná. Hledáme tedy řádky, ve kterých se poslední číslo liší od předposledního. K řešení poslouží grep s negovanou podmínkou (volba -v):

grep -v ' \([^ ]\+\) \+\1$'

Vzor začíná mezerou před předposledním číslem. Za ní následuje neprázdná posloupnost nemezerových znaků ([^ ]\+), která se zapamatuje. Po ní následuje alespoň jedna mezera ( \+) a znovu stejná posloupnost, za kterou už je jen konec řádku.

Použití při nahrazování

Daleko častěji se zapamatované řetězce vyskytují v příkazech pro nahrazování. Díky nim si můžete ze vstupních dat vytáhnout informace, které vás zajímají, a poskládat si je do tvaru, který potřebujete.

Příklad:

Běžný problém všedního dne: potřebujete u skupiny souborů změnit příponu z .htm na .html. Pro podobné účely sice existují různá udělátka, ale je třeba si je doinstalovávat a práce s nimi nebývá úplně snadná. takže se podívejme, jak poslouží standardní nástroje, které najdete v každém Unixu.

Postup je jednoduchý: obstaráte si seznam jmen souborů, každé jméno pak změníte na příkaz mv staré nové a tyto příkazy provedete. Popsaný postup lze realizovat třeba takto:

ls *.htm > seznam
sed 's/\(.*\)/mv \1 \1l/' seznam > akce
chmod a+x akce
./akce
rm seznam akce

Uznávám, že prosté připojení „l“ na konec jména souboru je dosti snadnou modifikací. Složitější věci však znamenají jen úpravu příkazu s. Například změnu přípony z .doc na .txt by zajistilo s/\(.*)\.doc/mv \1­.doc \1.txt/ – zapamatuje si jen vlastní jméno souboru a přípony jsou explicitně vyjmenovány.

Příklad:

A teď něco drsnějšího. Chtěl bych ze souboru /etc/passwd vyrobit seznam domácích stránek uživatelů. Takže potřebuji řádky transformovat z původní podoby

uživatel:heslo:UID:GID:vlastní jméno:...

na

<A HREF="/~uživatel">vlastní jméno</A>

Kýženým substitučním příkazem, který to zařídí, je

s/\([^:]*\):\([^:]*:\)\{3\}\([^:]*\).*/<A
HREF="/~\1">\3<\/A>/

Jak vidíte, prostřednictvím závorek lze předepsat počet opakování i rozsáhlejšímu úseku regulárního výrazu - zde se třikrát opakuje skupina [^:]*:. V takovýchto situacích závorky většinou neslouží k zapamatování (i když si pochopitelně něco zapamatují), ale čistě k vymezení opakované části. Ta má - bez ohledu na skutečný počet opakování - jen jediné pořadové číslo. Proto se závěrečné zapamatované jméno uloží jako \3.

Problémem regulárních výrazů je, že jsou velmi kompaktní. Vyznat se ve výše citovaném substitučním příkazu zabere chvíli času (zpravidla více, než jej vymyslet). V Perlu si můžete vytvoření seznamu rozložit: nejprve řádek rozkrájíte v místě výskytu dvojteček a z výsledného pole pak použijete první a pátý prvek (indexy 0 a 4):

while ( $radek = <> ) {
    @uzivatel = split(/:/, $radek);
    print "<A HREF=\"/~$uzivatel[0]\">";
    print "$uzivatel[4]</A>\n";
}

Program uložte třeba do souboru htmlseznam a spusťte

perl htmlseznam /etc/passwd

Shrnutí

výraz význam
\(výraz) zapamatuje si text vyhovující výrazu
\1 první zapamatovaný řetězec
Našli jste v článku chybu?
Vitalia.cz: Opuncie je plod kaktusu. Pozor na trny

Opuncie je plod kaktusu. Pozor na trny

Lupa.cz: Patička e-mailu závazná jako vlastnoruční podpis?

Patička e-mailu závazná jako vlastnoruční podpis?

Podnikatel.cz: Udělali jsme velkou chybu, napsal Čupr

Udělali jsme velkou chybu, napsal Čupr

Vitalia.cz: dTest odhalil ten nejlepší kečup

dTest odhalil ten nejlepší kečup

Lupa.cz: Jak se prodává firma za miliardu?

Jak se prodává firma za miliardu?

Lupa.cz: Další Češi si nechali vložit do těla čip

Další Češi si nechali vložit do těla čip

Vitalia.cz: 5 pravidel proti infekci močových cest

5 pravidel proti infekci močových cest

DigiZone.cz: Ginx TV: pořad o počítačových hráčích

Ginx TV: pořad o počítačových hráčích

120na80.cz: Hrbatá prsa aneb mýty o implantátech

Hrbatá prsa aneb mýty o implantátech

Podnikatel.cz: Chystá se smršť legislativních novinek

Chystá se smršť legislativních novinek

Podnikatel.cz: Byla finanční manažerka, teď cvičí jógu

Byla finanční manažerka, teď cvičí jógu

Podnikatel.cz: Znáte už 5 novinek k #EET

Znáte už 5 novinek k #EET

Podnikatel.cz: EET pro e-shopy? Postavené na hlavu

EET pro e-shopy? Postavené na hlavu

DigiZone.cz: Wimbledon na Nova Sport až do 2019

Wimbledon na Nova Sport až do 2019

Vitalia.cz: Kterou dýni můžete jíst za syrova?

Kterou dýni můžete jíst za syrova?

Podnikatel.cz: Babišovi se nedá věřit, stěžovali si hospodští

Babišovi se nedá věřit, stěžovali si hospodští

DigiZone.cz: Koncesionářské poplatky pro RTVS

Koncesionářské poplatky pro RTVS

Lupa.cz: Aukro.cz mění majitele. Vrací se do českých rukou

Aukro.cz mění majitele. Vrací se do českých rukou

DigiZone.cz: Numan Two: rozhlasový přijímač s CD

Numan Two: rozhlasový přijímač s CD

DigiZone.cz: Rapl: seriál, který vás smíří s ČT

Rapl: seriál, který vás smíří s ČT