Pravděpodobně mnozí z vás znají použití zástupných znaků „*“ a „?“, například při vyhledávání souborů. Jistě uznáte, že použití takových zástupných znaků (wildcards) je často neocenitelné, přesto nemusí být dostačující, pokud chceme postihnout komplikovanější řetězec. Právě k tomu se nám mohou hodit regulární výrazy.

Mnohým z vás je možná výše uvedený text povědomý. Není divu. Úvodní odstavec jsem si dovolil převzít z prvního článku série Perl-compatible regulární výrazy v PHP, který vycházel před nedávnem na Intervalu. Když jsem se proto rozhodl napsat také několik článků o regulárních výrazech v JavaScriptu, stál jsem před rozhodnutím, zda tuto sérii koncipovat zcela stejně (tedy popisovat kompletní problematiku regulárních výrazů a jejich aplikaci v příslušném programovacím jazyce) nebo předpokládat znalost tvorby regulárních výrazů a omezit se jen na popis prostředků pro práci s regulárními výrazy v JavaScriptu. Rozhodl jsem se pro kompromis.

Úvodem se podíváme na konstrukce regulárních výrazů (ovšem již mnohem stručněji, proto začátečníkům v práci s regulárními výrazy doporučuji nejdříve si přečíst články
Perl-compatible regulární výrazy v PHP – základní konstrukce a Perl-compatible regulární výrazy v PHP – praktické příklady) a v dalších článcích se již budu věnovat jednotlivým metodám (funkcím) pro práci s regulárními výrazy. Vzhledem ke skutečnosti, že část věnující se samotným regulárním výrazům bude spíše shrnutím problematiky než jejím vysvětlováním, v příslušných místech se budu odkazovat na podrobnější vysvětlení příslušného tématu v článcích o perl-compatible regulárních výrazech v PHP.

Protože česká (a do značné míry ani anglická) terminologie týkající se regulárních výrazů není jednotná, budu se držet názvosloví, které jsem používal v předchozích textech.

Regulární výrazy jsou speciální textové řetězce, které popisují určitou masku (vzor), 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 nějaké křestní jméno, a naším úkolem je najít jména začínající na „P“, která jsou delší než pět písmen. Pro takové zadání (řetězec začíná na „P“ a zároveň je delší než pět 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. Vráceny tak budou řádky se jmény Pankrác, Patricie, Patrik, Pavlína a podobně, ale již ne Petr, Pavel, Petra či Ondřej, Daniel a jiné.

Regulární výrazy se používají v mnoha programovacích jazycích (Perl, Java, C#, Javascript, PHP a další). Běžně je možné se setkat s dvěma typy regulárních výrazů:

  • POSIX
  • Perl-compatible

Regulární výrazy používané v JavaScriptu vycházejí z Perlu a tak jejich syntaxe je perlovským výrazům (či perl-compatible regulárním výrazům používaným v PHP) velmi podobná. Některé vlastnosti regulárních výrazů (tvrzení o předcházejícím, komentáře, podmíněné subvýrazy a podobně) však budeme muset v JavaScriptu oželet.

Implementace práce s regulárními výrazy je v různých verzích JavaScriptu různá. Verze 1.1 a nižší nepodporují regulární výrazy vůbec. Od verze 1.2 je zavedena podpora regulárních výrazů, verze 1.3 přidala objektu RegExp metodu toSource, od verze 1.5 je zaveden „multiple lines“ modifikátor, líné kvantifikátory, závorky netvořící zpětné reference a tvrzení o následujícím. V následujícím textu (nebude-li uvedeno jinak) budeme předpokládat verzi 1.5, která by měla být podporována Microsoft Internet Explorerem 6 a vyšším (respektive funkčnost implementované verze JScriptu by měla zhruba odpovídat JavaScriptu 1.5) a Gecko-based prohlížeči (Mozilla, Firefox, Netscape 6 a vyšší). Stručné shrnutí podpory jednotlivých verzí JavaScriptu v prohlížečích najdete například na stránce Geekpedia – Programming tutorial: An introduction to JavaScript.

Jak již bylo zmíněno, regulární výrazy jsou textové řetězce. Některé znaky však plní v textovém řetězci speciální funkci – takové znaky označujeme jako metaznaky. Jsou to \, ^, $, ., [, ], |, (, ), ?, *, +, {, }. Pokud chceme použít metaznak v jeho původním významu, je třeba před něj doplnit zpětné lomítko \. Takže pokud chceme hledat například řetězce obsahující a+b, musíme použít regulární výraz a\+b.

Shrňme si nyní použití metaznaků při konstruování regulárních výrazů v několika přehledových tabulkách:

\ vrací metaznaku jeho původní význam při použití v regulárním výrazu
. (tečka) zastupuje jeden libovolný znak (kromě znaku nového řádku)

Malý příklad:

  • Pokud chceme vytvořit regulární výraz, kterému bude odpovídat řetězec 2*3=6, musíme použít regulární výraz 2\*3=6 (protože * je metaznakem).
  • Regulárnímu výrazu a.b budou odpovídat řetězce aab, abb, aZb a podobně.
? minimálně 0krát, maximálně 1krát
* minimálně 0krát (maximálně neomezeno)
+ minimálně 1krát (maximálně neomezeno)
{n} právě nkrát
{m,n} minimálně mkrát, maximálně nkrát
{m,} minimálně mkrát (maximálně neomezeno)
?? minimálně 0krát, maximálně 1krát
*? minimálně 0krát (maximálně neomezeno)
+? minimálně 1krát (maximálně neomezeno)
{m,n}? minimálně mkrát, maximálně nkrát
{m,}? minimálně mkrát (maximálně neomezeno)

Malý příklad:

Nenasytné a líné kvantifikátory se liší následovně. Pokud máme řetězec aaa a regulární výraz a+, regulárnímu výrazu bude odpovídat aaanenasytné chování. Pokud máme stejný řetězec aaa, ale porovnáme jej s výrazem a+?, výsledkem bude jen alíné chování. Podrobnější (a názornější) vysvětlení rozdílu líných a nenasytných kvantifikátorů naleznete v článku Perl-compatible regulární výrazy v PHP – modifikátory a líné kvantifikátory.

\0 NUL znak
\t tabulátor
\n line feed (nový řádek)
\v vertikální tabulátor
\f form feed
\r carriage return
\xnn znak zadaný dvouciferným hexadecimálním číslem (ASCII)
\unnnn znak zadaný čtyřciferným hexadecimálním číslem (Unicode)
\cX řídící znak (control character); X je z rozsahu A-Z; například \cK je control-K
[abc] skupina znaků daná výčtem znaků (v tomto případě abc)
[a-z] skupina znaků daná intervalem (v tomto případě a-z)
[^abc] doplněk skupiny znaků dané výčtem znaků (v tomto případě jakýkoliv znak kromě abc)
[^a-z] doplněk skupiny znaků dané intervalem (v tomto případě jakýkoliv znak kromě znaků z intervalu a-z)
\d číslice 0-9
\D jakýkoliv znak kromě číslic 0-9
\w znaky „slova“ (ekvivalentní zápisu a-zA-Z0-9_)
\W jakýkoliv znak kromě znaků „slova“ (ekvivalentní zápisu ^a-zA-Z0-9_)
\s „bílé“ znaky (ekvivalentní zápisu \f\n\r\t\v\u00A0\u2028\u2029)
\S jakýkoliv znak kromě „bílých“ znaků (ekvivalentní zápisu ^ \f\n\r\t\v \u00A0\u2028\u2029)

Malý příklad:

  • regulárnímu výrazu [ab]{2,5} bude odpovídat 2-5 znaků dlouhý řetězec tvořený pomocí znaků a a b, například tedy ab, abbaa, aaa, bbbb
  • regulárnímu výrazu [1-5]+ bude odpovídat číslo tvořené minimálně jednou číslicí, přičemž povolené číslice jsou pouze 1, 2, 3, 4, 5
  • regulárnímu výrazu \d{4} bude odpovídat sekvence právě čtyř číslic desítkové soustavy, například 1289 či 0054 (takový regulární výraz bychom mohli například použit při kontrole správnosti, respektive smysluplnosti zadání nějakého PIN kódu)
  • regulárnímu výrazu \w{6} bude odpovídat sekvence právě šesti znaků (alfanumerické znaky doplněné o znak podtržítka), například Adam84 či jan_novak (takový regulární výraz bychom mohli například použit při kontrole správnosti, respektive smysluplnosti zadání přihlašovacího jména do nějakého systému)
^ začátek řetězce nebo řádku
$ konec řetězce nebo řádku
\b hranice „slova“
\B jakákoli pozice, kromě pozic, které jsou hranicí „slova“

Malý příklad:

  • regulárnímu výrazu ^1\d{3} odpovídají čtyřciferná čísla (respektive spíše sekvence číslic) začínající jedničkou, která jsou na začátku řetězce či řádku
  • regulárnímu výrazu \d{4}0$ odpovídají pěticiferná čísla (respektive spíše sekvence číslic) končící nulou, která jsou na konci řetězce či řádku

Podrobnější popis problematiky hranic (takzvaných shod nulové délky) naleznete v článku Perl-compatible regulární výrazy v PHP – hranice (pozor však na to, že v PHP je množina hranic větší).

a|b alternativy – rozdělení výrazu na dvě části (a a b), přičemž dojde-li ke shodě alespoň s jednou částí výrazu, uvažuje se, že řetězec regulárnímu výrazu odpovídá
(x) subvýraz – řetězec odpovídající části výrazu (x) (subvýrazu) ohraničené kulatými závorkami je zapamatován pro pozdější použití (zpětná reference)
(?:x) uzávorkování netvořící zpětné reference – výraz v závorce (x) je vyhodnocen, ale řetězec odpovídající této části výrazu není zapamatován
\n zpětná reference – de facto zastupuje řetězec odpovídající příslušnému subvýrazu; n je číslo subvýrazu
a(?=b) pozitivní tvrzení o následujícím (viz článek Perl-compatible regulární výrazy v PHP – lokální modifikátory, tvrzení)
a(?!b) negativní tvrzení o následujícím (viz článek Perl-compatible regulární výrazy v PHP – lokální modifikátory, tvrzení)

Malý příklad:

  • Regulárnímu výrazu ahoj|nazdar odpovídá právě jedna z možností pozdravu (tedy řetězec ahoj nebo řetězec nazdar).
  • Regulárnímu výrazu (\d)\d\1 odpovídá sekvence tří číslic, přičemž první a poslední číslice musí být shodné (zpětná reference se totiž odkazuje na první znak řetězce); vyhovovat tak budou například řetězce 050, 868 či 222, ale již ne 123.
  • Regulárnímu výrazu (?:ab)+ bude vyhovovat minimálně dvouznakový řetězec složený z (nepovinně se opakující) sekvence znaků ab; například tedy ab, abab, ababab, ale již ne ababa. (Pozn. aut.: Regulární výraz (?:ab)+ se od výrazu (ab)+ liší jen tím, že v případě výrazu (?:ab)+ není uchován (zapamatován) řetězec odpovídající výrazu v závorkách a nelze se tak na něj odkazovat pomocí zpětné reference.)
Tento článek byl původně publikován na serveru Interval.cz, kde naleznete
originální verzi článku.

Nenech si to pro sebe...

Pokud tě článek zaujal, sdílej ho s ostatními. Díky!