25.1.2014
Vyhledávání regulárních výrazů [REGEXP]
Stručný návod
Pokud jste se dosud nesetkali s termínem regulární výrazy, pak vězte, že se jedná o skvělé rozšíření možností jednoduchého vyhledávání v textu. Každý programovací jazyk či textový editor nabízí jiné možnosti, a také obvykle používá trochu odlišnou syntaxi. Pokud pracujete s rozsáhlejšími texty, a potřebujete v nich vyhledávat a upravovat, pak vám vřele doporučuji naučit se s regulárními výrazy pracovat. Pro nástin pochopení jejich užitečnosti vám mohu říci, že bez jejich alespoň částečné znalosti, by ve Studijní on-line bibli byla touto dobou sotva 1/5 překladů...
Regulární výrazy jsou mocným nástrojem pro hledání textových řetězců pomocí vzorů. Speciální předdefinované metaznaky dovolují specifikovat, zda hledaný řetězec leží na začátku nebo na konci textu / řádku, umožňuje specifikovat znaky, který řetězec obsahuje apod.
Jednoduché znaky: každý znak zastupuje sebe. Je to jako při normálním hledání. Pokud zadáte slovo jelen, bude se hledat "jelen". Escape sekvence začíná znakem
\. Existují některé speciální znaky, např.
^, který znamená, že se bude hledat na začátku řádku. Pokud ale budete chtít přímo znak
^, musíte zadat
\^. Podobně pro hledání samotného znaku
\ je třeba zadat
\\.
Možnosti používání regulárních výrazů v SOB jsou velmi omezené, přesto však jejich využitím můžete dosáhnout výsledků, kterých by nebylo možno dosáhnout jednoduchým vyhledáváním. V SOB využívám pro vyhledávání funkci MySQL "
REGEXP", což je asi jediná podpora regulárních výrazů při databázových dotazech. Tato funkce podporuje pouze následující metaznaky:
Metaznaky - znaky se zvláštní funkcí
^ (stříška - symbol mocnitele) : začátek řádku
$ (string - symbol dolaru) : konec řádku
. (tečka) : jakýkoliv znak (písmeno, číslice, oddělovače i mezera či tabulátor)
Čítače - volba počtu výskytů předchozího znaku
* (hvězdička - symbol násobení) : žádný nebo více výskytů znaku, podobně jako {0,}
+ (plus - symbol sčítání) : jeden nebo více výskytů znaku, podobně jako {1,}
? (otazník) : žádný nebo jeden výskyt znaku, podobně jako {0,1}
{n} : přesně n výskytů
{n,} : nejméně n výskytů
{n,m} : nejméně n-krát, ale ne víc než m-krát
Skupiny - (třídy) znaků
Je možné specifikovat skupiny znaků. Tato skupina je uzavřena v hranatých závorkách
[]. Text bude nalezen, pokud vyhovuje jeden znak ze seznamu. Pokud první znak za závorkou je
^, znamená to, že v textu nesmí být obsažen žádný znak ze seznamu. Příklady:
jel[aei]n : najde slova "jelan", "jelen", "jelin"
jel[^ai]n : najde slovo "jelen", ale nenajde "jelin", "jelan" atd.
Znak
- (mínus) znamená rozsah znaků. Potom a-z znamená jakýkoliv znak v rozsahu "a" až "z". Pokud chcete hledat přímo znak "-", musíte použít sekvenci \-. Příklady:
[-az] : vyhovují znaky "a", "z" a "-"
[az-] : vyhovují znaky "a", "z" a "-"
[a\-z] : vyhovují znaky "a", "z" a "-"
[a-z] : vyhovují všechny znaky v rozsahu "a" až "z"
[a-zA-Z] : vyhovují všechna malá a velká písmena v uvedeném rozsahu
[0-9] : vyhovují všechna čísla
Metaznaky - alternativy
Ve vzoru lze také specifikovat sérii alternativ, jako oddělovač slouží znak
| (svislá čára). Příklady:
jelen|jelan|jelin : najde "jelen", "jelan" nebo "jelin" (podobně jako jel(e|a|i)n)
Nezapomeňte ale na to, že znak | uvnitř [] je interpretován jako obyčejný znak!!!)
Pokud tedy uvedete
[jelen|jelan|jelin], ve skutečnosti jste zadali, že chcete hledat jeden ze znaků "jeln|ai"
Příklad správného použití alternativ:
jel(en|ínek) najde "jelen" nebo "jelínek"
Praktická ukázka použití Chceme např. zjistit orientační počet výskytů slov související s tématem "
věčnost". Víme, že v bibli jsou použity překlady jako "věčný", "věkovitý", "navěky", "věky věků" apod. Pokud budeme hledat společnou část, narazíme rychle na hned několik problémů. Prvním problémem je, že máme dvě společné části - "věč" a "věk". To znamená, že budeme muset provést minimálně 2 hledání, a výsledky nějak sloučit. To je však ten jednodušší problém. Druhý problém je mnohem závažnější. Společná část "věk" totiž není pouze součástí hledaných slov "věky a věků", ale i slov "věk" a také např. "člověk". Co tedy s tím? Tady pomohou jedině naše oblíbené regulární výrazy... :-)
Hledaný výraz:
- potřebujeme najít varianty s "věk" i "věč":
- zkusíme hledat např. v celé Bibli 21, a vyzkoušíme vyhledávací výraz
vě(č|k)- výsledkem je 1320 nalezených výskytů, včetně slov jako "věk" nebo "člověk". Své hledání budeme muset trochu upřesnit...
- upravíme svůj dotaz o možná následující písmena: za "věč" může následovat pouze "n", za "věk" nás zajímají pouze písmena "y", "ů" a případně "o".
- takže nový dotaz upravíme takto:
vě(č|k)(n|o|ů|y)- počet vyhledaných míst se snížil na 517, a vidíme, že se nám zobrazuje skutečně pouze požadované téma věčnosti.
- výraz ještě můžeme kosmeticky vylepšit o možnost zvýraznění předložky "na" v případě slova "navěky":
- takže dotaz by mohl vypadat např. takto:
(na)?vě(č|k)(n|o|ů|y)- pro připomenutí: otazník znamená žádný nebo jeden výskyt předchozího znaku, což je v tomto případě obsah závorky (na)
- počet výskytů zůstal stejný (517), jen nyní máme příjemněji zvýrazněny hledané výrazy
- můžeme dokonce zajistit zvýraznění celých slov obsahujících hledaný řetězec či řetězce - např. takto:
(na)?vě(čn|ky|ků|ko)[^ \.,;:!\?<]*- jak vidíte, upravil jsem alternativy na dvojznakové - na hledání
(čn/ky/ků/ko) namísto dvou jednopísmenných podmínek
- na konci je příkaz pro vyhledání zbytku slova - cokoli kromě mezery, tečky, čárky, středníku, dvojtečky, vykřičníku, otazníku a symbolu "
<", což je symbol začátku HTML tagů. Všimněte si, že před metaznaky "
." a "
?" je nutné vložit zpětné lomítko "
\"!
No řekněte sami - není to nádhera? :-)
Praktická ukázka použití č.2 Nyní nás pro změnu bude zajímat všechno, co se týká konkrétního věku. V tomto případě nebude možné vytvořit dokonalý výraz pro vyhledání pouze požadovaných informací, ale i tak nám regulární výrazy dramaticky usnadní práci. Budeme muset vyhledávat výraz "
věk" a také výraz "
let". Bohužel ne každý výskyt výrazu "let" se bude týkat věku - např. délky života.... :-(
Hledaný výraz:
- abychom omezili hledání výrazu "věk" pouze na téma věku, stačí před výraz vložit mezeru "
věk"
- zkusíme hledat např. opět v celé Bibli 21
- výsledkem je 136 nalezených výskytů, bohužel však včetně slov jako "
věky" nebo "
věků". Své hledání budeme muset trochu upřesnit...
- upravíme svůj dotaz tak, že zakážeme následující písmena - o, ů a y. Vyhledávací výraz nyní vypadá takto: "
věk[^ůyo]"
- počet vyhledaných míst se snížil na 56, a vidíme, že se nám zobrazuje skutečně pouze požadované téma věku.
- nyní potřebujeme přidat ještě hledání výrazu "let".
- výraz by mohl vypadat nějak takto:
( věk[^ůyo])|( let)- počet výskytů vzrostl na 417, ale lepší už to asi nebude :-)
Praktická ukázka použití č.3 Nyní nás budou zajímat místa v bibli, kde je číselnou hodnotou uveden počet let. Úkol je pro regulární výrazy velice jednoduchý, znamená to najít číslo následované mezerou a výrazem "
let". Výraz by tedy mohl vypadat např. takto:
[0-9]+( let) nebo
[0-9]{1,}( let)vysvětlení:
- výraz
[0-9]+ znamená, že chceme najít všechna libovolně dlouhá čísla (+ za výrazem znamená minimálně 1 výskyt, max. není omezeno)
- výraz
[0-9]{1,} znamená totéž, co předchozí varianta. Zjistil jsem však, že v SOB tyto výrazy někdy poskytují trochu odlišné výsledky!
- výsledkem hledání v celé Bibli 21 je 63 výskytů
- co kdyby nás však zajímaly pouze délky kratší než 100 let? Stačilo by hledat pouze jedno a dvoumístná čísla...
- obvykle máme více možností řešení. Můžeme např. hledat cokoli kromě čísla, následované jedno až dvoumístným číslem: "
[^0-9][0-9]{1,2}( let)"
- jednodušší možností je hledat mezeru, následovanou jedno až dvoumístným číslem: "
[0-9]{1,2}( let)"
- počet výskytů bude v obou případech 13.
- ale co když jsou některé číselné údaje psány čísly a jiné přepisem, nebo jsou pouze přepisem?
- finální dotaz by mohl vypadat např. takto: "
[^ ]{2,20}( let)[^ \.,;:!\?\)<]*" (na začátku je mezera!)
- díky tomuto výrazu budou nalezeny všechny zmínky o letech, včetně číselných i nečíselných hodnot, a včetně případné koncovky za "
let".
- ale časový údaj může mít i formu roků namísto let. Uděláme proto stejnou podmínku i pro roky...
- finální výraz by tedy mohl vypadat nějak takto: "
( [^ ]{2,20}( let)[^ \.,;:!\?\)<]*)|( [^ ]{2,20}( rok)[^ \.,;:!\?\)<]*)"
Hledání v překladech s poznámkami a Strongovými čísly Pokud budete hledat pomocí regulárních výrazů v překladech s poznámkami či Strongovými čísly, doporučuji Vám nezapomenout ošetřit konec slova od HTML tagů. Např. Strongova čísla jsou v databázi umístěna bez mezery za daným slovem, uzavřená mezi dvě hvězdičky (např. Bůh*H430*), zkratka morfologie řeckého textu je uzavřená mezi složené závorky (např. {N-GSF}) a na aktivní kód se vše převádí až v SOB - právě pomocí regulárních výrazů. Podobně odkaz na poznámky je u některých překladů realizován formou horního indexu hned za příslušným slovem - tedy tagem <sup> (pozn.). Vzhledem k tomu, že všechny HTML tagy začínají symbolem
<, mělo by stačit při hledání konec hledaného výrazu ošetřit podmínkou
[^<], resp. tuto podmínku přidat do stávající - např.
[^ \.,;:!\?\)<]*.
+ nebo {1,} ? -
+ obvykle přináší stejné výsledky jako
{1,} (minimálně 1 výskyt, maximálně bez omezení), ale zjistil jsem, že v SOB tomu tak vždy není. Vyzkoušejte prosím sami a porovnejte výsledky!
[ae] nebo (a|e) ?- stejná situace jako v předchozím případě. Obvykle oba výrazy přináší stejné výsledky, ale zjistil jsem, že v SOB je spolehlivější varianta s klasickými závorkami. Preferujte proto prosím variantu
(a|e) !!!
Pro snazší pochopení práce s regulárními výrazy jsem připravil videonávod:
Vyhledávání v SOB s pomocí regulárních výrazů (22:09 minut)
Libor Diviš
(autor SOB)