Regulární výrazy

Článek byl převzat z časopisu CoDe

Možná jste již někdy viděli něco jako

    ^[\w-]+(?:\.[\w-]+)*@(?:[\w-]+\.)+[a-zA-Z]{2,7}$

Tento "text" je příkladem regulárního výrazu. Možná vás napadne, že je to spíš "neregulerní" výraz, vzhledem k tomu, že na první pohled nedává žádný smysl. Tento regulární výraz mimochodem popisuje asi 99% e-mailových adres. Zkuste jej projít ještě jednou po přečtení tohoto článku. Pokud jej dokážete projít, budete ovládat regulární výrazy :-).

Co jsou regulární výrazy ?

Regulární výrazy (ve vývojářské hantýrce nazývané také regexpy) umožňují efektivně hledat v řetězcích pomocí vzoru. Většina programátorů zná prohledávání souborů pomocí vyhledávací masky (wildcard). Už jste někdy na disku hledali soubory *.mp3 ? Pak jste uživatelem regulárních výrazů!

Regulární výrazy je možné využít ve všech případech, kdy potřebujeme manipulovat s řetězci - vyhledávání, nahrazování. Je možné je použít také pro test, zda zadaný string vyhovuje určitému vzoru (např. zda se jedná o platnou mailovou adresu). Ve velké míře jsou jsou použity např. v programech na filtrování pošty.

Regulární výrazy je obecně snadnější napsat než analyzovat (podobně jako některé konstrukce v C++ :-)). Jenom ostřílení profesionálové poznají, že výraz

    ((\(\d{3}\) ?)|(\d{3}-))?\d{3}-\d{4}

znamená masku pro telefonní čísla ve Spojených státech, tedy např. (999) 999-9999.

Konstrukce regulárních výrazů - shoda se znaky

Ke konstrukci regulárních výrazů se používají tzv. metaznaky. Při hledání v textu pomocí regulárních výrazů se testuje shoda metaznaků s prohledáváným textem. Dejme tomu, že chceme prohledat text dokumentu na výskyt slova Windows. Regulární výraz pro hledání tohoto slova bude prostě

    Windows

Tento regulární výraz nevypadá složitě, ale neumožňuje ani žádné pokročilé hledání. Do regulárního výrazu přidáme metaznak "." pro shodu s jakýmkoliv znakem s vyjímkou nového řádku:

    W.ndows

Tento regulární výraz umožňuje hledat výrazy "Windows", "Wyndows", "W1ndows", "W-ndows" a jiné. Pokud potřebujeme na dané pozici specifikovat vzor pro pouze některé znaky, uzavřeme jejich výčet do hranatých závorek "[ ]", tedy například

    Windo[uvw]s

Uvedený regulární výraz se shoduje s "Windows", "Windous" a "Windovs". Neshoduje se ale s "Windos", protože na uvedené pozici musí být právě jeden znak. V hranatých závorkách je možné použít také rozsah znaků (podle ASCII tabulky), tedy např.

    Windows[1-4]

(shoduje se s "Windows1", "Windows2", "Windows3" a "Windows4", ale ne s Windows6 nebo Windows96). Nejčastěji používané rozsahy jsou [a-z], [A-Z] a [0-9].

Možná vás právě napadla otázka "Jak je to s rozlišováním malých a velkých písmen?". Ano, regulární výrazy rozlišují malá a velká písmena, ale dají se sestavit i tak, aby je nerozlišovaly.

Kromě zadání rozsahu je možné zadat i negativní podmínku pro rozsah znaků, tedy znaky, které na dané pozici být nemají. Příkladem je výraz

    Windows[^5-9]

který se shoduje s "Windows0", "WindowsA", "Windowsa", "Windows!" ale neshoduje se s "Windows5" nebo "Windows8".

Další možností je možnost použít znaku pro nebo, tedy "|". Výraz

    [L|l]inux

umožňuje nalezení slova "Linux" nebo "linux". Nenajde ale slovo "inux", protože na první pozici musí být písmeno "L" nebo "l".

Shoda s pozicí v řetězci

Regulární výrazy podporují dva znaky pro určení zda se hledaný vzor má hledat na začátku nebo na konci řetězce. Příkladem je výraz

    ^Windows

který vyhledá pouze řetězcem na jejichž začátku je slovo Windows. Souhlasí tedy s "Windows zabírají čím dál tím více místa na disku", nesouhlasí ale s textem "Za poslední rok byla ve Windows nalezena spousta chyb".

Metaznak "$" se používá ke hledání podřetězc na konci textu. Např pomocí výrazu

    [M|m]ac[O|o]s$

najdeme texty "MacOs", "macos" nebo "Nová verze systému MacOs", nenajdeme ale text "MacOs běží na počítačích Apple".

Oba uvedené metaznaky je možné i kombinovat. Představme si inventární číslo, které se skládá ze dvou písmen a dvou číslic. Pro jeho shodu použijeme výraz

    ^[A-Za-z][A-Za-z][0-9][0-9]$

Tento výraz se shoduje s "AB12", "CD99", neshoduje se ale s "9A99". Možná si říkáte, proč nepoužít uvedený výraz bez "^" a "$", tedy

    [A-Za-z][A-Za-z][0-9][0-9]

který by měl fungovat taky. Máte pravdu, ale pomocí tohoto výrazu byste našli i "ABCD1234", protože čtyři znaky uprostřed (CD12) se shodují se vzorem.

Shoda s opakováním znaků

Při specifikaci regulárního výrazu máme možnost dokonce specifikovat počet opakování daného znaku/daných znaků. Nejjednodušší možností je zadat za znakem nebo metaznakem "?", který znamená žádný nebo jeden výskyt znaku:

    Windows?

S tímto regulárním výrazem souhlasí řetězce "Windows" a "Window". Podobný je metaznak "+", který znamená jeden nebo více výskytů:

    Windows+

(Souhlasí s "Windows", "Windowsss" nebo "Windowsssssssssssss", ale ne s "Window"). Zde je vhodné připomenout, abychom nepletli znak "+" se spojovacím operátorem.

Znak "*" znamená 0 nebo libovolně velký počet opakování předcházejícího znaku/znaků. S výrazem

    Windows*

souhlasí "Window", "Windows" nebo "Windowsssssssss" (Ne ale Windowz !!!). Metaznaky pro opakování lze kombinovat třeba s metaznaky rozsahu, takže např.

    ^Linux[123]*$

bude souhlasit s "Linux", "Linux1", "Linux111111122222233333333", ale ne s "Linux3ova", "Toto je Linux12" (slovo Linux musí být na začátku) nebo s "Linuxx" (Na konci musí být pouze kombinace číslic 1, 2, a 3).

Ostatní metaznaky

Jak zapsat samotný metaznak, např. hvězdičku nebo otazník ? Regulární výrazy k tomuto účelu používají obrácené lomítko "\":

    BeOS\?

bude souhlasit s "BeOS?", ne ale s "BeOS".

Dalším fíglem je použítí kulatých závorek, které mohou specifikovat více znaků, např.

    ^Li(nux)*$

bude souhlasit s "Linux", "Linuxnux", "Linuxnuxnux", ale nebude souhlasit s "Linus". Při použití složených závorek můžeme zadat nejmenší a největší počet opakování zadaného znaku nebo skupiny znaků:

    Mac(OS){1,3}

(souhlasí s MacOS, MacOSOS a MacOSOSOS).

    Mac(OS){2,}

(souhlasí s MacOSOS, MacOSOSOS a MacOSOSOSOSOSOSOSOS, ale ne s MacOS).

Shrnutí metaznaků

Následující tabulka zobrazuje stručný přehled metaznaků použitelných v regulárních výrazech

Metaznak Význam
Shoda se znakem
. Jakýkoliv jeden znak kromě znaku "nový řádek"
[ ] Jeden znak ze seznamu v hranatých závorkách. Je možné použít i rozsah oddělený pomlčkou
x|y Znak "x" nebo "y"
x|y Znak "x" nebo "y"
Shoda s pozicí
^ Začátek řetězce
$ Konec řetězce
Opakování znaků
? 0 nebo 1 výskyt předcházejícího znaku
+ 1 nebo více výskytů předcházejícího znaku
* 0, 1 nebo více výskytů předcházejícího znaku
( ) Skupina znaků
{n} Přesně n opakování předcházejícího znaku
{n,} Nejméně n opakování předcházejícího znaku
{n,m} Nejméně n a nejvíce m opakování předcházejícího znaku
Speciální znaky
\ Následující znak není metaznak regulárního výrazu ale normální znak
\s Metaznak pro tzv. bílou mezeru (whitespace), tedy mezera, tabulátor, LF nebo FF.
\w Alfanumetický znak, tedy [0-9A-Za-z].
\W Ne-alfanumetický znak, tedy vše kromě [0-9A-Za-z].
\d Číslice, tedy [0-9].
\D Vše kromě číslic.
\f Znak FF (form feed)
\n Znak LF (line feed)
\r Znak CF (carriage return)
\t Znak tabulátoru
\v Znak vertikálního tabulátoru
\b Oddělení slova, tedy např. mezera.

Příklady vyhledávacích vzorů

Následující příklady uvádějí některé regulární výrazy

Výraz Význam
^[\w-]+(?:\.[\w-]+)*@(?:[\w-]+\.)+[a-zA-Z]{2,7}$ E-mailová adresa
^http://([a-zA-Z0-9_\-]+)([\.][a-zA-Z0-9_\-]+)+([/][a-zA-Z0-9\~\(\)_\-]*)+([\.][a-zA-Z0-9\(\)_\-]+)*$ URL odkaz
^(?:(?:25[0-5]|2[0-4]\d|[01]\d\d|\d?\d)(?(\.?\d)\.)){4}$ IP adresa v desítkovém tvaru (např. 128.25.1.135)
^(([a-zA-Z]:)|(\\{2}\w+)\$?)(\\(\w[\w ]*.*))+\.(jpg|JPG)$ Jméno souboru s příponou JPG
http://www.regexlib.com Odkaz na stránku obsahující stovky dalších (a složitějších) regulárních výrazů
CodeGuru Odkaz na článek o regulárních výrazech, obsahující také zdrojový kód třídy RegExp
RegExp Stažené zdrojové kódy třídy RegExp (v případě, že by ty na CodeGuru zrušili :-))