Perl-compatible regulární výrazy v PHP (1) – základní konstrukce

Regulární výrazy jsou speciální textové řetězce, které popisují určitou „masku“, které má odpovídat určitý textový řetězec. Nejlépe bude, když předcházející větu vysvětlím na příkladu. Řekněme, že máme soubor s textem, kde na každém řádku je jméno nějakého města, a naším úkolem je najít města, jejichž název začíná na „P“ a přitom je delší než 5 písmen. Pro takové zadání (řetězec začíná na „P“ a zároveň je delší než 5 znaků) vytvoříme příslušný regulární výraz. Takový regulární výraz se pak porovná postupně s každým řádkem a řádky, kde došlo ke shodě regulárního výrazu s příslušným textem na řádku, mohou být vráceny jako výsledek.

Regulární výrazy se používají v mnoha programovacích jazycích (například Perl, Java, C#, JavaScript, PHP), v pokročilejších editorech pro práci s textem (tím myslím editory takzvaného „plain-textu“, nikoliv textové editory typu Microsoft Word) a v operačním systému Unix/Linux téměř všude.

Běžně je možné se setkat s dvěma typy regulárních výrazů:

  • POSIX
  • Perl-compatible

Výše zmíněné dvě odnože regulárních výrazů se liší částečně syntaxí, ale především možnostmi tvorby komplikovanějších výrazů. Výrazům typu POSIX jsme se již (v souvislosti s použitím v PHP) na Intervalu věnovali v sérii článků Regulární výrazy v PHP podle POSIX. PHP ovšem umožňuje také práci s Perl-compatible regulárními výrazy (PCRE), které jsou ještě mocnější než POSIX. A protože i některé základní stavební prvky PCRE mají oproti POSIX jiné chování, půjdeme v poznávání PCRE krůček po krůčku hezky od začátku.

Na závěr trošku delšího úvodu musím doplnit, že ačkoli budeme dále používání PCRE demonstrovat na použití v PHP, drtivá většina uváděných skutečností bude platná při použití libovolného nástroje (programu, programovacího jazyka) pracujícího na bázi Perl-compatible regulárních výrazů.

V tomto úvodu se budu v zájmu srozumitelnosti i pro úplného začátečníka záměrně dopouštět jistých zjednodušení, která později v pravém místě doplním kompletním vysvětlením.

Výrazu abc budou odpovídat všechny řetězce, které obsahují abc. Tedy například abcddd, eeeabcfff, eabc a podobně.

Regulární výrazy mohou jednak obsahovat běžné znaky (jako jsou například číslice a písmena), ale také znaky se zvláštním významem – takzvané metaznaky. Mezi metaznaky patří \, ^, $, ., [, ], |, (, ), ?, *, +, {, }. Pokud chceme použít metaznak v jeho původním významu, je třeba před něj doplnit zpětné lomítko \. A tak pokud chceme hledat například řetězce obsahující a+b, musíme použít regulární výraz a\+b.

Metaznak . (tečka) zastupuje jeden libovolný znak (kromě znaku konce řádku \n). Výrazu a.c budou odpovídat například řetězce abc, aDc, a+c a podobné – nikoli však již ac.

Kvantifikátory jsou konstrukce, které udávají, kolikrát se má znak (bezprostředně předcházející kvantifikátoru) vyskytovat. Základními kvantifikátory jsou ?, + a *. Pomocí číselných intervalů zadaných ve složených závorkách pak lze specifikovat počet výskytů precizněji. Tak například regulárnímu výrazu abcd{2,4}ee odpovídají všechny řetězce obsahující abc bezprostředně následované sekvencí minimálně dvou a maximálně čtyř písmen d, která jsou bezprostředně následována řetězcem ee.

Kvantifikátor Počet výskytů Příklady odpovídající regulárnímu výrazu abkvantifikátorc
? minimálně 0krát, maximálně 1krát ac, abc
* minimálně 0krát (maximálně neomezeno) ac, abc, abbc, abbbc
+ minimálně 1krát (maximálně neomezeno) abc, abbc, abbbc
{n} právě nkrát abbbbc (pro n=4)
{m,n} minimálně mkrát, maximálně nkrát abbc, abbbc, abbbbc (pro m=2, n=4)
{m,} minimálně mkrát (maximálně neomezeno) abbc, abbbc, abbbbbbbc (pro m=2)

Jak je z tabulky patrné, kvantifikátory ?, + a * lze nahradit ekvivalentním zápisem pomocí složených závorek.

? {0,1}
* {0,}
+ {1,}

Často je třeba určit, že požadovaný text má být na začátku či konci prohledávaného řetězce (textu). Regulárnímu výrazu ^ab budou odpovídat všechny řetězce začínající sekvencí znaků ab, tedy například ab, abab, abxyz a podobně. Naopak regulárnímu výrazu xyz$ budou odpovídat řetězce končící sekvencí znaků xyz, tedy například xyz, xxxyz, x1x2xyz a podobně.

V druhém kroku jsme použili metaznak tečka, který zastupuje libovolný znak. Co když však chceme určit, že se na daném místě smí vyskytovat jen určitá množina znaků? Seznam znaků, které takovou množinu tvoří, jednoduše uzavřeme do hranatých závorek ([]). Regulárnímu výrazu a[bc]d bude odpovídat řetězec abd a acd – ne však už abcd nebo ad, protože mezi znaky a a d musí být právě jeden znak. Na pořadí znaků v hranatých závorkách nezáleží a tak [jkl] plní zcela stejnou funkci jako [klj] či [lkj].

Kromě možnosti definovat množinu povolených znaků, můžeme také definovat množinu zakázaných znaků. V takovém případě hned za levou hranatou závorku doplníme znak stříšky ^. Regulárnímu výrazu a[^bc]d pak budou odpovídat řetězce obsahující mezi znaky a a d právě jeden libovolný znak, kromě znaků b a c.

Pokud je požadovanou množinou znaků určitý interval znaků (například znaky aj) není třeba psát [abcdefghij] – stačí zapsat interval jako [a-j]. Analogicky lze definovat i množinu zakázaných znaků (například [^a-j]). Do hranatých závorek můžeme dokonce zapsat i několik intervalů znaků za sebou – například zápisu [a-zA-Z] odpovídají všechna malá i velká písmena anglické abecedy.

V prvním kroku jsme si vyjmenovali metaznaky (tedy znaky před které je třeba napsat zpětné lomítko, pokud je chceme uvnitř regulárního výrazu použit v jejich běžném významu). Uvnitř skupin znaků existuje ovšem trošku jiná skupina metaznaků, a to znaky \, ^, -, ]. Pokud chceme tyto znaky použít v jejich běžném významu, je třeba, aby jim opět předcházelo zpětné lomítko nebo aby se příslušný znak vyskytoval ve skupině znaků na místě, kde si ho algoritmus zpracovávající regulární výrazy nemůže vykládat jako speciální znak. Tedy ^ se jako běžný znak bude chovat kdekoli ve skupině znaků kromě první pozice (hned za otevírací hranatou závorkou), ] se jako běžný znak bude chovat naopak pouze, pokud bude zapsán hned za otevírací hranatou závorkou, a - se bude chovat jako běžný znak, pokud bude na první či poslední pozici ve skupině znaků. Pokud chceme, aby skupina znaků obsahovala například znaky a, b, c, -, [ a ], můžeme zapsat tuto skupinu znaků jako [abc\-[\]] nebo jako [][abc-].

Pro často používané množiny znaků existují předdefinované zkratky, které je možno použít místo seznamu znaků v hranatých závorkách. Regulárnímu výrazu \d{3} bude odpovídat řetězec tří po sobě následujících číslic desítkové soustavy (0-9). Tedy řetězce 123, 584, 012 – ale již ne 15, protože sekvence číslic musí být tři znaky dlouhá.

Tabulka ukazuje předdefinované množiny znaků a jejich zkratky:

Zkratka Ekvivalentní zápisu Popis
\d [0-9] číslice desítková soustavy
\D [^0-9] cokoli kromě číslic desítkové soustavy
\w [0-9a-zA-Z_] znaky tzv. „slova“ (word characters)
\W [^0-9a-zA-Z_] cokoli kromě znaků „slova“ (non-word characters)
\s [ \n\r\t\f] tzv. „bílé znaky“ (whitespace characters) – mezera, line feed, carriage return, tab, form feed
\S [^ \n\r\t\f] cokoli kromě „bílých znaků“

Dalším metaznakem je „svislítko“, či chcete-li „ořítko“ (odvozeno od anglického slova or=nebo) – tedy znak |. Tímto znakem můžeme regulární výraz rozdělit na části. Pokud řetězec (porovnávaný s regulárním výrazem) odpovídá alespoň jedné části, znamená to, že odpovídá výrazu jako celku. Regulárnímu výrazu ahoj|nazdar|nashledanou|sbohem tak odpovídá kterýkoli z uvedených čtyř pozdravů.

Doposud jsme předpokládali, že kvantifikátory udávají (pouze) počet výskytů bezprostředně předcházejícího znaku (ať již určeného přímo, pomocí metaznaku „.“ či skupinou znaků). Pomocí kulatých závorek můžeme v rámci regulárního výrazu vytvářet takzvané subvýrazy. Pokud kvantifikátor následuje bezprostředně za subvýrazem (tedy za pravou kulatou závorkou, která subvýraz uzavírá), počet výskytů se týká celého řetězce, který odpovídá patřičnému subvýrazu. Například regulárnímu výrazu ^(www\.)?mojefirma.cz$ odpovídá řetězec mojefirma.cz i www.mojefirma.cz.

Subvýrazy se mohou také hodit, pokud chceme kvalitněji pracovat s alternativami. Řekněme, že chceme vytvořit regulární výraz, kterému by odpovídalo pouze Veselé Velikonoce a Veselé Vánoce. Na první pohled se může zdát, že takovému zadání vyhoví regulární výraz ^Veselé Velikonoce|Vánoce$. Není tomu tak, protože svislítko rozdělí regulární výraz na dvě části, na ^Veselé Velikonoce a Vánoce$. Přičemž první části bude odpovídat libovolný řetězec začínající textem Veselé Velikonoce a druhé části bude odpovídat libovolný řetězec končící textem Vánoce. Správným řešením je totiž regulární výraz ^Veselé (Velikonoce|Vánoce)$, protože zde jsou alternativy vyhodnocovány pouze v rámci subvýrazu.

Subvýrazy mají ještě mnohá další využití. Na ně se však podíváme později.

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

Share
Published by
Miroslav Pecka
Tags: phpregexp

Recent Posts

GA4 (not set) problém & jeho řešení

Jak se zbavit (not set) v Session Source a Session Medium?

2 roky ago

Měření QR kódů a offline zdrojů do Google Analytics

Chcete doměřit efekt vaší offline reklamy, ze které vedete lidi na váš web? Jde to…

2 roky ago

5+1 věcí, které se online markeťák může naučit od ajťáka

Tenhle článek jsem měl rozepsaný fakt dlouho, ale je stále aktuální… Trápí mě, že opakovaně…

4 roky ago

Profesionál v onlinu: řemeslo + kontext + přesahy

Poslední dobou jsem se setkal s pár majiteli malých firem, kteří mají web a snaží…

4 roky ago

Nástroje pro tvorbu screencast videí

Občas se mě někdo ptá, co používám pro tvorbu screencastů a online videí. Které nástroje…

4 roky ago

7 Google Analytics video návodů pro efektivnější práci

V rámci 5 videí najdete 7 krátkých video tipů pro zefektivnění práce s Google Analytics…

5 roky ago