Nedávno jsem se věnoval vcelku obšírně perl-compatible regulárním výrazům a jejich nasazení v PHP a nyní došlo i na JavaScript. Tentokrát se již nebudeme tolik zabývat obecným konstruováním výrazů, ale pustíme se, po krátkém úvodu, přímo do jejich praktického použití v JavaScriptu.

Koncepce série

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.

Co jsou to regulární výrazy?

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 regulárních výrazů v JavaScriptu

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.

Tvoříme regulární výrazy

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:

Zpětné lomítko, tečka

\ 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ě.

Nenasytné kvantifikátory

? 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)

Líné kvantifikátory

?? 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.

Znaky zapisované pomocí escape sekvencí

\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

Skupiny znaků

[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)

Hranice – aneb ukotvení (částí) řetězců

^ 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ší).

Další konstrukce

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.)

Odkazy, zdroje

Starší komentáře ke článku

Pokud máte zájem o starší komentáře k tomuto článku, naleznete je zde.

Žádný příspěvek v diskuzi

Odpovědět