Regulárne výrazy
Štýlovanie menu a tabuľky bolo celkom zábavné a prinieslo to pekný výsledok. Na našej tretej stránke si spravíme kontaktný formulár na kontaktovanie – odoslanie mailu pre používateľa, niečo podobné sme už robili. Teraz si ho aj pekne naštýlujeme, ale okrem toho bude obsahovať ešte nejaké veci, ktoré by mal každý formulár mať a doteraz sme ich nepoužili. Na to, aby sme to mohli zrealizovať, si však musíme prebrať jednu informatickú tému, pre vás sa to bude zdať ako kus matiky, ale nemôžeme sa tomu vyhnúť, pretože v programovaní sa to veľmi často používa na rôzne účely. Bude to pre vás po pekných kapitolách o štýlovaní tabuľky a menu asi nezažívne, ťažké a nezaujímavé, ale musíme si tým prejsť, potom budú zase krajšie kapitoly;-)
Čo to teda ten regulárny výraz znamená? Slovo regulárny popisuje niečo v zmysle pravidiel – podľa pravidiel, resp. podľa predpísaných - predom daných pravidiel. Sám regulárny výraz reprezentuje v podstate nejaké pravidlo, ktoré si my určíme a vieme sa pomocou neho počítača spýtať, či nejaký vstup (text/obsah premennej) spĺňa naše pravidlá, čiže či je to taká hodnota, akú sme očakávali, akú chceme. To akú chceme hodnotu dostať popíšeme tým pravidlom – regulárnym výrazom. Aký to má význam?
Spomeňte si na formulár na odoslanie mailu. Mali sme tam napríklad položku email, ktorú mal používateľ zadať, aby sme mu odoslali mail. Z praxe je viac než jasné, že akonáhle môže niečo vyplniť používateľ, treba to ošetriť, lebo vždy môže zadať chybu alebo zadať niečo zle. My, ako programátori vieme, čo očakávame. Má to byť správna emailová adresa zadaná v tvare meno, zavináč, mail, bodka, doména. Používateľ sa však môže hocikedy pomýliť alebo zadať zle adresu. My by sme mali vždy pred vykonaním akcie s týmto vstupom skontrolovať, či je to to, čo sme očakávali. Ak áno, tak nech sa vykoná to čo má a ak nie, tak nevykonať akciu, pretože by sa nepodarila a dať najavo používateľovi, že niečo zle vyplnil alebo že ním zadané dáta nie sú v poriadku.
Pomocou regulárneho výrazu vieme zadať akokeby predpis, čo má nami očakávaná hodnota obsahovať, resp. z čoho – z akých presne zadaných znakov má pozostávať. Na základe tohto výrazu vieme pomocou funkcie na to určenej zistiť, či daná hodnota spĺňa podmienky výrazu. Výrazy môžu byť ľubovoľné, vieme nimi popísať akýkoľvek predpis ako napr. iba čísla, iba čísla od 5 po 8, aby to malo iba tri znaky, aby to obsahovalo zavináč, aby to neobsahovalo medzeru, aby to malo len veľké písmená, aby to bolo číslo 5 a za ním 2 písmená a pod. Jednoducho, akúkoľvek očakávanú kombináciu vieme zadať pomocou regulárnych výrazov. Regulárne výrazy majú viacero predpisov a špeciálnych znakov, ktoré každé reprezentujú nejaké pravidlo a môžeme ich medzi sebou kombinovať.
Existuje viacero typov regulárnych výrazov, dva najznámejšie sú Perl-compatible (PCRE - kompatibilné s Perl) a POSIX. Jednotlivé typy sú dosť podobné, odlišujú sa však troška v syntaxi a v istých pravidlách, my sa budeme učiť PCRE. Regulárny výraz musí byť ohraničený nejakým oddeľovačom (delimiter), môže to byť lomítko (/), mriežka (#), perceto (%), zátvorky ([]) a iné...my budeme používať mriežku (#). Reťazec znakov medzi dvomi mriežkami bude pre nás znamenať regulárny výraz. Regulárne výrazy majú niekoľko tzv. meta-characterov (meta-znakov), ktoré slúžia na špecifikovanie nejakej vlastnosti výrazu. Pomocou nich vieme vyskladať kompletný výraz, ktorý popisuje naše požiadavky. Vysvetlovať v texte jednotlivé znaky a ich význam je asi zbytočné a neprehľadné, ukážeme si to rovno na ukážke v súbore page3.php, ktorú si následne rozoberieme:
page3.php:
<?php
$regex_value_array = array();
//konkretna hodnota - retazec
$regex_value_array[] = array('#suhlasim#', 'nie');
$regex_value_array[] = array('#suhlasim#', 'suhlasim');
//^ - zaciatok retazca
$regex_value_array[] = array('#^abc#', 'abcdef');
$regex_value_array[] = array('#^abc#', 'bcdef');
//$ - koniec retazca
$regex_value_array[] = array('#5$#', '12345');
$regex_value_array[] = array('#5$#', '1234');
//[] - rozsah
$regex_value_array[] = array('#[1-5]#', '872asdfasf');
$regex_value_array[] = array('#[1-5]#', '987898789asdfsafd');
$regex_value_array[] = array('#^[a-z]#', '872asdfasf');
$regex_value_array[] = array('#^[a-z]#', 'a872asdfasf');
//{} - kvantifikatory
$regex_value_array[] = array('#^[a-zA-Z0-9]{2}$#', 'a7');
$regex_value_array[] = array('#^[a-zA-Z0-9]{2}$#', 'UI9');
$regex_value_array[] = array('#^[a-zA-Z0-9]*$#', '');
$regex_value_array[] = array('#^[a-zA-Z0-9]*$#', 'G');
$regex_value_array[] = array('#^[a-zA-Z0-9]*$#', 'Gsdf345ffR');
$regex_value_array[] = array('#^[a-zA-Z0-9]+$#', '');
$regex_value_array[] = array('#^[a-zA-Z0-9]+$#', '12');
//hocico, alebo, nesmie obsahovat
$regex_value_array[] = array('#^ano|nie$#', 'fero');
$regex_value_array[] = array('#^ano|nie$#', 'ano');
$regex_value_array[] = array('#^ano|nie$#', 'nie');
$regex_value_array[] = array('#^.b$#', 'cg');
$regex_value_array[] = array('#^.b$#', ' b');
$regex_value_array[] = array('#^.b$#', 'b');
$regex_value_array[] = array('#^[^x]+$#', '2 3 b');
$regex_value_array[] = array('#^[^x]+$#', '2 3 b x sdd2');
$regex_value_array[] = array('#^.+@.+\..+$#', 'mail@mail.sk');
for($i = 0; $i < count($regex_value_array); $i++){
echo ($i+1) . ' - Regularny vyraz :';
echo '<b> '.$regex_value_array[$i][0].' </b>';
echo ' -> hodnota :<b> '.$regex_value_array[$i][1].' </b>';
echo '-> vysledok : <b>'.preg_match($regex_value_array[$i][0], $regex_value_array[$i][1]).'</b><br><br>';
}
Možno to vyzerá odplašujúco, ale nebojte sa, hneď všetko pochopíte. Čo sa týka kódu, na začiatku si vytvárame dvojrozmerné pole, kde každý prvok je dvojica (pole s dvomi prvkami) výrazov – prvý je regulárny výraz a druhý je nami zadaná hodnota (testovaná). Každá takáto dvojica bude slúžiť pre vysvetlenie, či hodnota spĺňa požiadavky regulárneho výrazu. Následne si cyklom for prechádzame toto pole a vypisujeme na obrazovku (do stránky) poradové číslo dvojice, výrazy a najmä výsledok funkcie preg_match, ktorá nám vracia odpoveď, či nami zadaný výraz (druhý parameter funkcie) spĺňa požiadavky regulárneho výrazu (prvý parameter funkcie). Takto budeme jasne vidieť, aký máme regulárny výraz a čo sme zadali a akú nám to vrátilo odpoveď. Číslo 1 reprezentuje kladnú odpoveď (zhodu) a 0 reprezentuje nesplnenie požiadaviek výrazu.
Ako sme povedali, medzi mriežky zadávame regulárny výraz. Ak zadáme iba konkrétne nejaké znaky (čísla, písmená, znamienka...), ktoré nie sú špeciálnou značkou pre regulárny výraz (meta-znak), zadávame pravidlo, aby hodnota obsahovala presne túto hodnotu – tento reťazec. V prvých dvoch príkladoch sme použili ako regulárny výraz reťazec znakov suhlasim. Tým určujeme, že hodnota musí obsahovať presne toto slovo – tento reťazec znakov. Ako vidíme, v prvom príklade sme zadali slovo nie, takže sme nesplnili požiadavky výrazu (odpoveď je 0), ale v druhom prípade sme použili presne toto slovo, takže sme podmienky splnili (odpoveď je 1). Ak by sme zadali aj viac znakov, ale nachádzalo by sa medzi nimi slovo suhlasim, bola by zhoda s regulárnym výrazom.
V ďalších dvoch (3-4) príkladoch sme použili znak striešky (^), ktorý sa na anglickej klávesnici nachádza na klávese číslo 6. Možno ste si všimli, že ak zadáte tento znak (shift+6), tak sa spraví akokeby malá strieška, za ktorou keď stlačíte šípku alebo iný znak, tak ostane taká malá. Ak však zadáte striešku a následne stlačíte medzerovník, ostane strieška väčšia. Na toto si dajte pozor, my potrebujeme veľkú striešku! Tento znak značí začiatok reťazca. To znamená, že ak za týmto znakom zadáme čokoľvek (uvidíte neskôr), značíme tým, že to musí byť na začiatku reťazca. Nami zadaný výraz hovorí, že očakávame, že hodnota bude začínať reťazcom abc, takže to môže byť abc a hocičo za tým. Ako je vidieť vo štvrtom príklade, slovo nezačína písmenami abc, takže nie je podmienka splnená. Podobne slúži znak dolár ($ - príklady 5-6), tento však označuje koniec reťazca. V nami zadanom výraze očakávame, že hodnota bude končiť číslom 5. Je jedno, čo je predtým, týmto sme zadali len podmienku, že to musí končiť päťkou.
Ďalej je možné do hranatých zátvoriek ([] - príklady 7-8) zadať rozsah znakov. My sme zadali výraz, ktorý hovorí, že hodnota musí obsahovať aspoň jeden znak z rozsahu 1 až 5. Upozornil by som, doteraz sme zadali čím musí výraz začať (3-4) alebo skončiť (5-6). V tomto prípade (podobne ako v 1-2) sme zadali iba rozsah, nedali sme znak začiatku ani konca, takže zadaním len zátvoriek s rozsahom hovoríme akokeby hociktorý aspoň jeden znak z tohto rozsahu. Nehovoríme, že musí byť na začiatku, nehovoríme ani že musí byť na konci a nehovoríme týmto ani to, že to musí byť len jeden znak. To znamená, že aby hodnota splnila tento predpis, musí obsahovať aspoň jeden znak z tohto rozsahu, ako je vidieť na príklade 7 (nachádza sa tam číslo 2). V ďalších dvoch príkladoch (9-10) sme použili znak začiatku, takže vravíme, že náš reťazec musí začínať nejakým znakom od a po z, čiže malým písmenom z abecedy. Rovnako sa to dá skombinovať aj so znakom pre koniec ($).
Ďalej sme použili viacero kvantifikátorov, čiže ukazovateľov, ktoré nám označujú počet očakávaných znakov. Pomocou množinových zátvoriek ({}) a číslom v nich určujeme, koľko presne znakov očakávame. V prvom príklade (11) sme zadali, že náš reťazec musí presne obsahovať 2 znaky (lebo sme dali aj začiatok aj koniec) zo zadaných rozsahov, takže malé/veľké písmeno alebo číslo. Toto je rozdiel oproti príkladom 7/8, ak by sme v tomto prípade nedali začiatok a koniec, tak by sme tým vraveli, že náš reťazec musí obsahovať dvojicu znakov z týchto rozsahov hocikde. My sme ale ohraničili začiatok a koniec, takže náš reťazec to nemôže obsahovať hocikde, ale musia to byť presne 2 znaky a zo zadaného rozsahu. V ďalšom príklade (12) sme zadali do množinových zátvoriek s čiarkou druhé číslo. V tomto prípade to znamená, že reťazec musí obsahovať najmenej dva znaky a maximálne 5.
Pre ľubovoľný počet znakov zadávame znamienko hviezdičky (* - príklady 13-15), ktorá určuje akokeby hocikoľko znakov z daného predpisu (0 a viac). Všimnite si, že v príklade 13 sme nezadali nič a aj tak je podmienka splnená, pretože hviezdička značí hocikoľko – nula a viac a nula je nič, takže to aj tak platí. Naopak znamienko plus (+) značí hocikoľko – ale najmenej jeden (1 a viac) znak tam musí byť. Preto je príklad 16 nesplený, lebo sme zadali nič.
Ďalej sme použili znamienko zvislej čiarka (| - príklady 18-20), ktoré značí prípad alebo. V našom výraze očakávame reťazec ano alebo nie. Ak príde niečo iné ako presne tieto dve hodnoty, nebude predpis splnený. V príkladoch 21-23 sme použili znak bodky (.), ktorý reprezentuje akýkoľvek znak. V našom výraze očakávame hocičo a za tým písmeno b. Všimnite si, že v príklade 22 sme zadali medzeru, čo je tiež znak a predpis je splnený, ale v príklade 23 sme nezadali nič a tak podmienka bodky – hocijakého znaku – nie je splnená, pretože síce bodka je splnená písmenom b, ale za ním nenasleduje konkrétne druhý znak, ktorý musí byť b.
Posledný metaznak (24-25) je strieška v hranatých zátvorkách, ktorá značí, že reťazec nemôže obsahovať tento znak. V našom prípade sme zadali, že hodnota nemôže obsahovať písmeno x. Podľa dvoch príkladov je vidieť, že skutočne vyhovujúcim sa stáva reťazec, ktorý písmeno x nemá.
Ako posledný regulárny výraz sme použili jednoduchšiu verziu overovania emailovej adresy. Nami zadaný výraz by sa dal preložiť takto – obsahuj aspoň jeden akýkoľvek znak (.+), za ktorým nasleduje zavináč (@), potom znova obsahuj akýkoľvek znak aspoň raz (.+), za ktorým nasleduje bodka (\.), za ktorou na konci nasleduje opäť hocijaký znak aspoň raz (.+). Tento výraz nie je správny pre overovanie emailovej adresy, pretože neobmedzuje viacero možných riešení. Nateraz to ale nejdeme riešiť, pretože to je zložitejší problém, pre nás bude zatiaľ stačiť tento. Možno ste sa spýtali, prečo pri overovaní, aby to malo bodku (.) sme zadali pred ňu spätné lomítko. Znak bodky je pri regulárnych výrazoch metaznak, takže aby sme odlíšili, že nechceme použiť bodku ako metaznak, ale ako samostatný znak bodky, musíme dať pred to spätné lomítko.
Ak ste niečo z toho nepochopili...to nevadí, na regulárne výrazy sa vyžaduje prax, aby to človek úplne chápal a aby bol v tom zbehlý. Ukázali sme si však základné a pre nás dôležité prípady, už budeme vedieť napríklad povedať, aby nejaký výraz mal presne toľko znakov alebo aby obsahoval niečo konkrétne, čo chceme a podobne. Hneď v ďalšej kapitole niečo z toho použijeme:)