Deprecated: Function set_magic_quotes_runtime() is deprecated in /mnt/data/accounts/w/webmark/data/www/blog/textpattern/lib/txplib_db.php on line 14
Perl-compatible regulární výrazy v PHP (7) - lokální modifikátory :: tvrzení :: Blog v pavučině
Blog v pavučině je mrtev. Ať žije nový blog Všeho s Mírou
Skočit na obsah Skočit na menu Skočit na vyhledávání

Perl-compatible regulární výrazy v PHP (7) - lokální modifikátory, tvrzení

Ačkoli jsme se modifikátorům věnovali již ve dvou článcích, vrátíme se k nim i tentokrát a předvedeme si možnost jejich lokálního použití. V druhé části článku pak nahlédneme pod pokličku jedné pokročilé techniky, kterou jsou takzvaná „tvrzení“.

V předcházejících článcích jsme se seznámili se šesti modifikátory ("i", "m", "s", "x", "e" a "U"). Vždy, když jsme použili určitý kvantifikátor, měl platnost pro celý regulární výraz. Mnohé modifikátory ("i", "m", "s", "x", "U") je však možno použít (pomocí speciální konstrukce) i lokálně, tedy tak, že změna chování se bude týkat jen ohraničené části regulárního výrazu.

Uveďme si příklad s modifikátorem "i" (ignore case - nerozlišování malých a velkých písmen). Regulárnímu výrazu ^ab(?i:cd)e$ budou odpovídat abcde, abcDe, abCde a abCDe (nikoli však již například ABCDE). Modifikátor "i" se v tomto výrazu týká pouze části výrazu mezi (?i: a ). Toto uzávorkování části výrazu netvoří zpětnou referenci.

Všimněte si, že konstrukce pro "uzávorkování netvořící zpětné reference" (viz předchozí článek) je velmi podobná. Místo i v (?i: je možno použít také modifikátory "m", "s", "x" a "U". Můžeme dokonce použít i několik modifikátorů současně - například "i" a zároveň "x" ve výrazu ^ab(?ix:c d)$ (díky modifikátoru "x" bude mezera mezi c a d ignorována).

Pro lokální definování modifikátorů však existuje ještě jedna syntaxe. Syntaxi si opět předvedeme na příkladu s použitím modifikátoru "i". Regulární výraz ^ab(?i)cd(?-i)e$ je ekvivalentní výše uvedeného výrazu ^ab(?i:cd)e$. Konstrukce (?i) zapne modifikátor "i" a ten bude platit až do konce řetězce (případně do konce subvýrazu jehož je součástí) nebo do jeho vypnutí.

Konstrukce pro vypnutí modifikátoru je podobná - v našem případě (?-i). Obecně se příslušný modifikátor vypne, pokud písmenu označujícímu daný modifikátor předchází znak - (pomlčka). Pomocí jedné konstrukce můžeme dokonce jeden modifikátor zapnout a druhý vypnout. Například konstrukce (?i-x) zapne modifikátor "i" a vypne modifikátor "x".

Pro úplnost doplním ještě třetí možnost zápisu výrazu, který bude ekvivalentem výše zmíněných dvou výrazů ^ab(?i:cd)e$ a ^ab(?i)cd(?-i)e$. Jedná se o výraz ^ab((?i)cd)e$ a demonstruje výše zmíněné tvrzení, že pokud je použita konstrukce (?i) v rámci subvýrazu, je platnost modifikátoru omezena na část příslušného subvýrazu od místa použití modifikátoru do konce subvýrazu.

Při používání konstrukce (?i) (samozřejmě nejen s modifikátorem "i") ve výrazu (či subvýrazu) s alternativami si musíme dát zvlášť pozor, aby se náš regulární výraz skutečně choval tak, jak zamýšlíme. Například regulárnímu výrazu ^(ab(?i)c|de)$ totiž odpovídají jednak řetězce abc a abC, ale také de, dE, De a DE. Je tedy zřejmé, že modifikátor ovlivní i druhou část (alternativu) subvýrazu.

Při popisu principů regulárních výrazů se snažím nalézt pro anglické termíny (popisující problematiku regulárních výrazů) také české ekvivalenty. Proto budu dále v textu pro look ahead assertions používat termín tvrzení o následujícím a pro look behind assertions termín tvrzení o předcházejícím. A o čem to je vlastně řeč?

Cílem použití regulárních výrazů je často nalezení textu (nebo části textu), který danému regulárnímu výrazu odpovídá. Výsledkem hledání je pak řetězec znaků o určité délce. Občas však můžeme chtít deklarovat, že za tímto řetězcem (nebo jeho částí) musí (či naopak nesmí) následovat určitý řetězec, který je opět popsán pomocí regulárního výrazu.

Řekněme, že chceme nalézt sekvenci minimálně tří znaků a (což popisuje regulární výraz a{3,}), za níž nenásleduje číslice desítkové soustavy (což popisuje výraz \d). Podstatné je uvědomit si, že výsledkem hledání bude pouze sekvence znaků a a o tom, co následuje za touto sekvencí, nám stačí vědět (tvrdit), že odpovídá danému tvrzení. V našem případě nám stačí vědět, že za sekvencí znaků a nenásleduje číslice desítkové soustavy (a nezajímá nás už, co za sekvencí následuje).

Právě jsme si popsali modelový příklad vhodný pro použití "záporného tvrzení o následujícím" (záporného proto, že uvádíme "...za níž nenásleduje..."). Popsaný příklad lze postihnout regulárním výrazem a{3,}(?!\d). Klíčovou je v tomto výrazu část (?!\d), která popisuje vlastní záporné tvrzení o následujícím. Vlastní regulární výraz popisující řetězec, který v určitém místě nesmí následovat (vyskytovat se vpravo od aktuální pozice), je uzavřen mezi (?! a ) (tyto závorky nevytvářejí zpětné reference).

Stejně jako existují záporná tvrzení o následujícím, existují také kladná tvrzení o následujícím. Pokud trochu upravíme výše uvedený příklad, můžeme chtít nalézt sekvenci minimálně tří znaků a (což popisuje regulární výraz a{3,}), za níž následuje číslice desítkové soustavy (což popisuje výraz \d). Potom použijeme regulární výraz a{3,}(?=\d). V tomto případě představuje kladné tvrzení o následujícím (?=\d). Mezi (?= a ) ovšem může být opět libovolný regulární výraz. I zde platí, že součástí nalezeného řetězce nebude (v našem případě) ona následující číslice desítkové soustavy. Dokonce ani nezjistíme, o jakou číslici se jedná, stačí nám tvrzení, že tam nějaká číslice je.

Tvrzení o předcházejícím funguje podobně jako tvrzení o následujícím, jen s tím rozdílem, že se "zkoumání" podrobuje řetězec vlevo od aktuální pozice (tedy - zjednodušeně řečeno - od pozice, kde se nachází tvrzení o předcházejícím).

Řekněme, že chceme najít číslo, které není beze zbytku dělitelné stem (nekončí tedy sekvencí 00). Řešením je použití "záporného tvrzení o předcházejícím ", které má obecnou konstrukci (?výraz), kde výraz je regulární výraz popisující předcházející řetězec. V našem případě bude celý regulární výraz \b\d+(?. Abychom zajistili, že výrazu budou odpovídat skutečně jen ryze číselné sekvence (nikoli části alfanumerických sekvencí jako například 041234 z řetězce VF041234), ohraničili jsme výraz pomocí hranic slova (\b). Za první hranicí slova následuje výraz \d+ (sekvence číslic), následovaný záporným tvrzením o předcházejícím (?.

Toto tvrzení říká skutečně jen tolik, že "vlevo od dané pozice se nesmí vyskytovat sekvence 00". Rozhodně neříká, že se vlevo od dané pozice musí vyskytovat sekvence dvou znaků, která není 00. Výrazu \b\d+(? proto bude odpovídat například i číslo 1, protože 1 odpovídá výrazu \d+ a v okamžiku, kdy "stojíme" za znakem 1, platí také, že předcházející řetězec (tedy 1) neodpovídá regulárnímu výrazu 00 (což je žádoucí, protože v regulárním výrazu je použito záporné tvrzení o předcházejícím).

I u tvrzení o předcházejícím existuje kladná varianta tvrzení, která má obecnou konstrukci (?<=výraz), kde výraz je regulární výraz popisující předcházející řetězec. Kladná varianta k předcházejícímu příkladu by byla nalezení čísla (respektive sekvence číslic) končícího sekvencí 00. V tomto případě se ovšem obejdeme při konstruování regulárního výrazu i bez speciální konstrukce kladného tvrzení o předcházejícím - stačí použít regulární výraz \b\d*00\b. V našem případě v kladné variantě tvrzení totiž přesně známe poslední dvě číslice čísla (poslední dva znaky řetězce).

Jak bylo zmíněno výše, tvrzení o následujícím může být reprezentováno libovolným regulárním výrazem. U tvrzení o předcházejícím však existuje jisté omezení. Povoleny jsou pouze takové regulární výrazy, kterým odpovídají řetězce pevné délky, což v praxi znamená nemožnost použití kvantifikátorů. Proto například zápis (?<=\d+) není přípustný. Použití alternativ o nestejné (avšak pevné) délce je povoleno. Proto například zápis (?<=ahoj|nazdar) je přípustný.

Shrňme si jednotlivé konstrukce v přehledné tabulce.

KonstrukceTvrzení
(?=výraz)kladné, o následujícím
(?!výraz)záporné, o následujícím
(?<=výraz)kladné, o předcházejícím
(?výraz)záporné, o předcházejícím

Pro všechny konstrukce typu kladného či záporného tvrzení o následujícím či předcházejícím platí, že závorky, v nichž je celá konstrukce uzavřena, netvoří zpětné reference.

Tento článek byl původně publikován na serveru Interval.cz, kde naleznete originální verzi článku.