Autor Téma: Vyhledávání pomocí regulárních výrazů  (Přečteno 8006 krát)

Libor Diviš

  • Administrator
  • Jr. Member
  • *****
  • Příspěvků: 89
  • Impossible is nothing ...
    • Zobrazit profil
    • O Bohu (About God)
  • Skype: libordivis
Vyhledávání pomocí regulárních výrazů
« kdy: Leden 27*, 2014, 07:51:44 dopoledne »
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)
« Poslední změna: Leden 27*, 2014, 08:41:45 dopoledne od Libor Diviš »