13. Regulární výrazy
1. Úvod
Regulární výrazy představují velmi silný nástroj k vyhledávání, analýze, filtrování, kontrole syntaxe, extrakci a transformaci textů a textových dat. Slouží k formálnímu popisu (obvykle nekonečné) množiny řetězců a následné vyhledávání řetězců z této množiny v textu. S takto vyhledanými řetězci (tzv. výskyty či shodami) můžeme dále pracovat.
Regulární výrazy se objevují se ve většině programovacích jazyků, ale také v textových editorech či manuálním nastavení e-mailových filtrů.
V linuxu se bohužel vyskytují tři různé syntaxe regulárních výrazů – základní regulární výrazy, rozšířené regulární výrazy a regulární výrazy jazyka Perl. Z praktických důvodů považuji za nejdůležitější rozšířené regulární výrazy, a proto kdykoliv napíšu „regulární výraz“ bez dalšího upřesnění, mám na mysli rozšířený regulární výraz. Kde se od sebe syntaxe liší, bude to u zaklínadel upřesněno; kde není uvedena samostatná varianta pro Perl, platí pro Perl varianta pro rozšířený regulární výraz.
GNU Bash je vyvíjen v rámci projektu GNU.
2. Definice
- Řetězec se shoduje s daným regulárním výrazem, pokud patří do množiny řetězců, kterou regulární výraz definuje, tedy pokud odpovídá požadavkům regulárního výrazu jako celek, od začátku do konce. Např. s regulárním výrazem „a.c“ se shoduje řetězec „abc“, protože „a“ v regulárním výrazu přijímá „a“ z řetězce; tečka z regulárního výrazu přijímá jakýkoliv znak, tedy i „b“ v řetězci a „c“ v regulárním výrazu přijme „c“ z řetězce. S tímtéž regulárním výrazem se už ale neshodují řetězce „Abc“ (pokud nevypnete rozlišování velkých a malých písmen), „abb“, „abbc“ či „abcc“ (protože k poslednímu „c“ už v regulárním výrazu není nic, co by ho přijalo).
- Shoda (match) je nejlevější a nejdelší (u tzv. „nehladového prohledávání“ naopak nejkratší) podřetězec prohledávaného řetězce, který se shoduje s daným regulárním výrazem, a totéž rekurzivně pro zbytek řetězce za koncem shody. Takže shody jsou vlastně podřetězce shodující se s regulárním výrazem, ale jen tak, aby se nepřekrývaly. Shodou může být i celý prohledávaný řetězec (protože každý řetězec je sám svým podřetězcem).
- Řetězec odpovídá danému regulárnímu výrazu, pokud s ním má nějakou shodu. Takže regulárnímu výrazu „a.c“ odpovídají např. řetězce „abc“, „aabc“, „xaxcx“, „xaxcxaxcx“ apod., ale ne „xaxbxc“.
- Jako atom označuji nejkratší část (podřetězec) regulárního výrazu, která končí na dané pozici a tvořila by syntakticky správný regulární výraz sama o sobě. Atomem je např. „a“, „[abc]“, „(a|b)?“ či „\s+“, ale ne „a|b“, protože „b“ je kratší a samo o sobě tvoří syntakticky správný regulární výraz.
- Kvantifikátor je speciální podřetězec, který se zapisuje za atom a určuje dovolený počet opakování.
- Kotva a hranice jsou speciální atomy k testování pozice, např. „^“ nebo „\<“. Z prohledávaného řetězce přijímají fiktivní prázdný podřetězec na jednoznačné pozici (u kotvy) nebo na všech pozicích splňujících určité podmínky (u hranice). Zvláštním případem hranice je vyhlížení.
- Vzorek bashe (také vzorek) je regulární výraz v interpretu bash zapsaný jeho zvláštní syntaxí; slouží nejčastěji k filtrování názvů souborů. Při testování vzorků se obvykle vyžaduje úplná shoda.
3. Zaklínadla: Regulární výrazy
3/1 Jednotlivé znaky
3/2 Kvantifikátory (operátory opakování)
3/3 Operátor „nebo“
3/4 Kotvy a hranice (pozice)
3/5 Seskupení
3/6 Paměť (omezená podpora)
3/7 Vyhlížení (jen Perl)
4. Zaklínadla: Vzorky bashe
4/1 Základní
4/2 Rozšířené (s kvantifikací)
Podvzorek v následujících zaklínadlech je jakýkoliv vzorek, který by byl platný sám o sobě.
5. Parametry příkazů
5/1 bash
Poznámka: Protože pravidla odzvláštňování regulárního výrazu v této konstrukci jsou neintuitivní a nepraktická, striktně doporučuji zadávat regulární výraz jako proměnnou, např. takto:
Pozor! Proměnnou s regulárním výrazem v této konstrukci nikdy neuzavírejte do uvozovek! Pokud to uděláte, bash ji bude interpretovat jako obyčejný řetězec, ne jako regulární výraz!
Existuje ještě druhý přiměřeně funkční způsob:
[[ řetězec =~ $(printf %s "regulární výraz") ]]
Ale v tomto případě nesmí regulární výraz končit znakem nového řádku \n, protože ten by se při rozvoji ztratil; pokud tímto znakem končit musí, pomůže uzavřít ho do hranatých závorek („[\n]“).
Příklad:
5/2 egrep
-v | Logická negace; hledat řádky, které nevyhovují výrazu. |
-x | Regulárnímu výrazu musí odpovídat celá řádka (výchozí chování: jakýkoliv podřetězec řádku). |
-z | Řádky vstupních souborů jsou ukončeny nulovým bajtem; znak \n bude považovat za za normální znak. |
-C počet | „kontext“ Kromě vyhovujícího řádku vypíše zadaný počet předchozích a následujících. (Samostatně lze tyto počty nastavit parametry -A a -B.) |
-o | Místo celých řádek vypisuje jednotlivé podřetězce vyhovující výrazu, každý podřetězec na samostatnou řádku. |
-h | Vyhledává-li se ve více souborech, neuvede se jako prefix řádky název souboru. |
-H | Vždy uvede jako prefix řádku název souboru. |
-n | Jako prefix bude vypisovat číslo řádky. |
-q | Žádný normální výstup, jen otestuje, zda by našel alespoň jeden vyhovující řádek. Parametr -s zase potlačí chybová hlášení. |
-m N | Ukončí hledání po nalezení N vyhovujících řádek. |
-i | Nerozlišovat velká a malá písmena. |
Poznámka: příkaz „grep“ má tytéž parametry jako „egrep“, ale pracuje se základními regulárními výrazy.
5/3 gawk
-F hodnota | Nastaví systémovou proměnnou „FS“ (field separator) na uvedenou hodnotu. |
-v proměnná=hodnota | Nastaví proměnnou na uvedenou hodnotu. |
-S | Spustí skript v „bezpečném režimu“, kdy nemůže volat příkazy bashe, spouštět jiné programy ani otevírat další soubory. |
5/4 perl
-n | Vykoná program v cyklu pro každou řádku vstupu. |
-p | Vykoná program v cyklu pro každý řádek vstupu a na konci každého cyklu vypíše proměnnou „$_“ ($ARG). |
-w | Zapne užitečná varování. (-W zapne všechna varování.) |
-e program | Vykoná tento program místo načtení programu ze souboru. |
-X | Vypne všechna varování. |
5/5 sed
-E | Používá rozšířené regulární výrazy místo základních. |
-i | „in-place“ Výstupem přepíše původní soubor. |
-z | Řádek končí nulovým bajtem, ne znakem \n. |
-u | „unbuffered“ Čte a zapisuje data po jednotlivých řádcích. (Normálně je kvůli výkonu načítá po delších blocích.) |
6. Instalace na Ubuntu
Příkazy „egrep“, „grep“, „perl“ a „sed“ jsou základními součástmi Ubuntu. Příkaz „gawk“ je nutné doinstalovat, nebo místo něj použít méně schopný příkaz „awk“, který je základní součástí Ubuntu.
Regulární výrazy jsou používány i v mnoha dalších programech.
Rozšířené vzorky bashe jsou ve výchozím nastavení zapnuty pouze v interaktivním režimu. Ve skriptech a jiných případech, kdy jsou vypnuty, je potřeba je zapnout příkazem:
7. Tipy a zkušenosti
- V Perlu se k označení desítkové číslice běžně používá podvýraz „\d“; v jiných syntaxích regulárních výrazů ovšem není podporován, proto doporučuji zvyknout si na podvýraz „[0-9]“, který je čitelnější a je podporovaný opravdu všude.
- Parametr -o u příkazu „egrep“ lze efektivně využít při počítání ne-ASCII znaků. Počet znaků č, š a ž v textu zjistíte příkazem „egrep -o '[čšž]' | wc -l“.
- Regulární výrazy jsou přezdívány „write-only language“, protože bývá výrazně snazší je napsat než přečíst a pochopit. Než začnete rozumět cizím regulárním výrazům, musíte získat značné zkušenosti s psaním svých vlastních.
- Ve výrazech typu „"[^"]*"“ často zapomínám kvantifikátor + či *.
- Příručka varuje, že testování komplikovaných rozšířených vzorků může být pomalé. Pokud na to narazíte, může váš skript urychlit, když je přepíšete na regulární výrazy a k jejich testování použijete externí příkaz jako např. „egrep“.
- Vzorky začínající „?“, „*“ nebo „[znaky]“ ignorují skryté soubory! Pravidlo říká, že obecné znaky jako „?“, „*“ a „[]“ nepokrývají tečku, která je prvním znakem názvu souboru. Toto chování lze vypnout nastavením „shopt -s dotglob“. Všechny názvy souborů a adresářů včetně „.“ a „..“ pokrývá rozšířený vzorek „?(.)*“.
8. Další zdroje informací
Pro samotné regulární výrazy je k dispozici velké množství webových stránek (viz Odkazy), ale většinou jsou zbytečné, protože s referenční příručkou (resp. s touto kapitolou) budete pravděpodobně schopen/a požadovaný regulární výraz vymyslet sám/a.
Pro zmíněné programy:
- Web regularnivyrazy.info
- Přednáška Lukáše Bařinky: Bash a regulární výrazy vs shellovské vzory
- Seriál Regulární výrazy v PHP (Perl-compatible)
- Seriál článků na Root.cz
- Článek Regulární výraz na Wikipedii
- Kniha: GOYVAERTS, Jan a Steven LEVITHAN. Regulární výrazy: kuchařka programátora. Brno: Computer Press, 2010. ISBN 978-80-251-1935-8.
- Online tester regulárních výrazů
- Přednáška Milana Davídka: Regulární výrazy
- Článek na blogu Miroslava Pecky
- Web Regular-Expressions.info (anglicky)
- Web RexEgg (anglicky)
- Online příručka GNU awk (anglicky)
- Online příručka GNU sed (anglicky)
- Online příručka modulu perlre (anglicky)
- Manuálová stránka: grep (anglicky)
- Manuálová stránka: pcrepattern (anglicky)
- Balíček „gawk“ (anglicky)
- Balíček „perl“ (anglicky)
- Balíček „sed“ (anglicky)
9. Zákulisí kapitoly
V této verzi kapitoly chybí:
- nic
Tato kapitola záměrně nepokrývá:
- nic