Hlavní navigace

Regulární výrazy (3)

18. 4. 2000
Doba čtení: 7 minut

Sdílet

První dvě části našeho krátkého seriálu se zabývaly těmi nejzákladnějšími prvky regulárních výrazů. Přestože jsem například v oblasti opakování ještě zůstal dost dlužen, rozhodl jsem se udělat malou přestávku a věnovat se programům, které regulární výrazy používají. Dnes se tedy pokusím popsat, co a jak se s nimi dá provádět.

grep a spol.

Rodina programů *grep slouží k vyhledávání v souborech. Typické použití: hledáte určitý identifikátor v haldě zdrojových kódů nebo chcete zjistit, odkud se spouští určitý program. Spuštění je prosté:

grep  vzor  seznam_souborů

Vzorem je regulární výraz. Výstup programu tvoří řádky, které vyhovují zadanému vzoru (což nejčastěji znamená, že obsahují zadané slovo). Pokud program zkoumá více než jeden soubor, vypíše zároveň před každý řádek název souboru, ze kterého pochází. Prostřednictvím voleb lze ovlivnit jeho chování. Těmi nejběžnějšími jsou:

Tabulka č. 43
-i nerozlišovat malá písmena od velkých
-w vybírat jen řádky, na nichž vzoru vyhovuje celé slovo
-v negovat výsledek (vypisovat řádky, které nevyhovují vzoru)
-l vypisovat jen jména souborů
-r rekurzivně procházet adresáře (umí jen některé verze grepu)

grep je představitelem celé rodinky programů. Mají podobná jména i funkci, liší se jen v detailech. Jejími standardními členy jsou tyto tři programy:

Tabulka č. 44
grep klasický grep, vzorem může být obyčejný regulární výraz
egrep vzorem je rozšířený regulární výraz (viz příště), používá rychlejší vyhledávací algoritmus
fgrep vzorem je jen obyčejný řetězec znaků; teoreticky nejrychlejší, ale praktická měření ukazují opak; zapomeňte na něj

Kromě nich existují ještě některé další pozoruhodné alternativy. Asi nejzajímavější je agrep (approximate grep) vyhledávající řetězce, které se zadanému vzoru pouze podobají. Najde například nejpodobnější nebo všechny takové, které se od vzoru liší jen v daném počtu znaků.

Vřele doporučuji používat grep a spol. pocházející z GNU projektu. Ve srovnání s klasickými implementacemi je rychlejší (používá lepší algoritmy) a navíc umí některé příjemnosti (třeba rekurzivní hledání). Zejména komerční verze Unixu však mají tendenci trvat na originálních verzích. Proto vyzkoušejte

grep --version

Dostanete-li chybové hlášení nebo se ohlásí někdo jiný než GNU grep, máte co instalovat.

sed

Program sed je neinteraktivní editor. Zadáte mu sadu příkazů a on podle nich zpracuje vstupní text. De facto se jedná o nástroj pro vytváření editačních filtrů.

Regulární výrazy se v sedu vyskytují hned ve dvojí roli. Lze je použít k vyhledání řádků, na které se má vztahovat určitý příkaz. Druhým místem výskytu regulárních výrazů je příkaz pro nahrazování, který má tvar s/vzor/náhrada/. Vyhledává regulárním výrazem zadaný vzor a pokud jej najde, vloží na jeho místo náhradu. Za závěrečné lomítko můžete připojit ještě volby. Tou nejpoužívanější je g (global), která zajistí nahrazení všech výskytů vzoru na řádku. Standardně se totiž nahrazuje jen první.

Příkazy sedu mají obecně tvar

řádky příkaz

Počáteční definice řádků určuje, na které řádky vstupního textu se příkaz bude vztahovat (chybí-li, znamená to všechny řádky).

Příklad:

Řekněme, že jste změnili doménu z kdesi.cz na jinde.cz. Navíc chcete ze svých WWW stránek odstranit pracovní texty - tedy veškeré úseky začínající <DIV CLASS="pracovni"> a končící </DIV>. Zajistí to následující dvojice příkazů:

s/kdesi\.cz/jinde\.cz/g
/<DIV CLASS="pracovni">/,/<\/DIV>/d

První je klasické nahrazení, které se bez určení řádků vztahuje na celý vstupní text. Druhým příkazem je d (delete), kterému podlehnou všechny skupiny řádků od řádku vyhovujícího prvnímu regulárnímu výrazu až po nejbližší následující, který vyhovuje druhému. Všimněte si, že lomítko v </DIV> je třeba chránit zpětným lomítkem, protože v příkazu s má speciální funkci oddělovače. Tuto dvojici uložíte řekněme do souboru zmena a každou stránku pak podrobíte příkazu

sed -f zmena

Příklad tiše předpokládá, že své HTML stránky píšete stejně jako já - tedy že značky <DIV> a </DIV> jsou na samostatném řádku. Pokud ne, vezme při mazání ze své celý řádek, který některou z nich obsahuje.

vi

Unixové textové editory, jejichž je vi nejklasičtějším představitelem, typicky používají regulární výrazy k vyhledávání textu a také k jeho nahrazování.

Konkrétně v případě vi zahajujete hledání stisknutím klávesy /, případně ? (pokud chcete hledat směrem k začátku dokumentu). Následně napište regulární výraz a stiskněte [Enter]. Kurzor poskočí na nejbližší následující/před­chozí řetězec vyhovující zadanému výrazu. Chcete-li se přesunout na další, stiskněte n (next) pokud chcete hledat stejným směrem či N pro hledání směrem opačným. Čili úplně stejně jako v programech more a less.

V kombinaci s tečkou (opakování poslední editační operace) tvoří n pořádně silnou dvojku. Pomocí n si poskakujete na další a další výskyty hledaného řetězce a tu a tam na některý zopakujete editační operaci stisknutím tečky.

Příkaz pro nahrazení má stejnou podobu, jako u editoru sed. Jediným rozdílem je, že pokud neuvedete řádky, implicitně se týká pouze aktuálního řádku. Chcete-li nahradit vzor v celém textu, použijte jako definici řádků znak %. Nahrazování patří do příkazového režimu, takže celý příkaz musíte zahájit dvojtečkou.

Příklad:

Výše zmiňovanou náhradu kdesi.cz na jinde.cz by ve vi zajistil příkaz

:%s/kdesi\.cz/jinde\.cz/g

Některé verze vi určené pro grafické prostředí (například gvim) nabízejí i uživatelsky přítulnou verzi tohoto příkazu - viz obrázek. Ortodoxní uživatel o ni samozřejmě nezavadí ani pohledem, protože

  1. Musí-li pracovat v textovém režimu nebo na jiném počítači, nebude tato lahůdka k dispozici.
  2. :s je rychlejší a u novějších implementací vi máte navíc historii příkazů.
  3. Jen při používání :s vypadáte jako skutečný borec.

reg

Pokud používáte vim, můžete k vyznačení řádků s výhodou využít vizuální režim. Stiskněte klávesu v a zvýrazněte požadované rozmezí řádků. Když potom stisknete :, editor automaticky vloží podivné rozmezí řádků ‚<,‘>, což znamená od prvního vyznačeného řádku po poslední. Pozor, nahrazení se týká celých řádků, ne jen vyznačeného textu v nich.

Ostatní editory nabízejí zpravidla podobné služby, liší se jen příkazy, kterými je vyvoláváte.

awk

Program awk realizuje specializovaný programovací jazyk, který umožňuje zpracovávat textové soubory s pevnou strukturou (např. /etc/passwd, výstup příkazu ls -l a podobně).

Regulární výrazy opět vystupují ve dvou známých rolích: určují řádky, kterých se operace týká, a vyskytují se v nahrazování.

Základním problémem awku je jeho jednostrannost. Syntaktický tvar programů je velmi nezvyklý a držet jej v hlavě kvůli úzce vymezené skupině úloh, které dokáže řešit, se podle mého soudu nevyplatí.

Perl

Perl je pozoruhodný programovací jazyk, který z hlediska regulárních výrazů nabízí dvě vysoce zajímavé vlastnosti:

  1. Má nejbohatší a nejpřehlednější sortiment regulárních výrazů ze všech mně známých nástrojů.
  2. Spojuje regulární výrazy s běžnými programátorskými konstrukcemi a la C.

Ve srovnání s Perlem vám sed či awk nemají mnoho co nabídnout. V poslední době pro neinteraktivní úpravy textů nepoužívám nic jiného.

Základní konstrukcí pro uplatnění regulárních výrazů v Perlu je operátor srovnání (=~). Jeho levým operandem je řetězec znaků (typicky proměnná). Pravý operand závisí na tom, co s dotyčným řetězcem hodláte provádět. Pokud vám jde o prosté srovnání, zda řetězec vyhovuje regulárnímu výrazu, zapíšete sem obvyklý vzor uzavřený mezi dvě lomítka.

root_podpora

Příklad:

Perl je velmi košatý a lze jej používat mnoha různými způsoby. Začnu s minimalistickou variantou, kdy prováděný „program“ píšete přímo do příkazového řádku. V tomto případě se Perl používá s volbou -e, která způsobí, že následující text bude interpretován rovnou jako příkazy jazyka. Zpravidla bývá kombinována s volbou -n, díky níž budou příkazy prováděny pro každý řádek vstupního textu. Takže třeba

perl -ne 'print if /Pepa/'
vypíše ty řádky vstupního textu, které obsahují řetězec „Pepa“. Zároveň zde perl demonstruje skutečnost, že si dokáže hodně věcí domyslet (chybí zde proměnná i operátor srovnání). Já osobně se však této domýšlivosti raději straním.

Následuje serióznější úryvek z programu. Vypíše hlášení, zda proměnná $radek obsahuje podřetězec „Pepa“.

if ( $radek =~ /Pepa/ ) {
    print "Je tam!";
} else {
    print "Není tam...";
}

Chcete-li použít regulární výraz k nahrazení řetězce jiným, uveďte na pravé straně srovnání obvyklou substituční konstrukci s/vzor/náhrada/.

Příklad:

A ještě jeden kondenzát. Následující volání nahradí ve vstupním textu všechna čísla (celá čísla, nikoli jednotlivé číslice!) znakem X

perl -pe 's/[0-9][0-9]*/X/g'

Použitá volba -p se podobá -n, ale po provedení příkazů navíc pošle aktuální řádek na standardní výstup. Všimněte si apostrofů, kterými jsou obklopeny regulární výrazy a příkazy na příkazovém řádku. Zajišťují, že je interpret příkazů předá v nezměněné podobě volanému programu a nebude se snažit interpetovat je jako své speciální znaky.

V programech se substituce zpravidla používá takto:

$radek =~ s/[0-9][0-9]*/X/g;
Zde se nahrazuje v obsahu proměnné $radek.

Jak vidíte, použití regulárních výrazů na proměnné obsahující řetězce znaků je v Perlu velmi snadné. Zajímavé služby nabízí také funkce split(/vzor/,­řetězec), která řetězec znaků rozdělí na části ohraničené výskyty zadaného regulárního výrazu. Ideální pro zpracování souborů, jako je /etc/passwd.

Byl pro vás článek přínosný?

Autor článku

Pavel Satrapa působí na Ústavu nových technologií a aplikované informatiky na Technické univerzitě v Liberci, píše knihy a motá se kolem tuzemské akademické sítě CESNET.