Lekce 6 - Regulární výrazy v Pythonu
V minulé lekci, Iterátory podruhé: Generátory v Pythonu, jsme si vytvoříli vlastní iterátor, seznámili se s generátory a prozkoumali jejich výhody.
V následujícím tutoriálu kolekcí v Pythonu učiníme cimrmanovský krok stranou a zaměříme se na regulární výrazy. Do kolekcí je řadíme proto, protože je využijeme zejména při zpracovávání velkého objemu dat a textů. A pro to jsou kolekce stvořené. Regulární výrazy nám umožňují v Pythonu provádět analýzu textu. Je to metoda, pomocí které se na základě vzoru rozpoznávají nebo získávají data.
Regulární výrazy
Regulární výrazy vznikly z důvodu potřeby práce s textovými řetězci určitým unifikovaným způsobem. Jsou zajímavým nástrojem nejen pro ověření, zdali zadaný textový řetězec splňuje určená pravidla (validace), ale také nám umožňují vyhledávat určité podřetězce. Zbavíme se tak mnohdy i několika vnořených podmínek.
Regulární výrazy nám také pomůžou s filtrováním dat z formuláře, vyhledáváním v textu nebo zpracováváním řetězců. Nejčastěji je využijeme při ověřování položek formuláře. S regulárními výrazy se dá dělat plno věcí, například zvýrazňovat slova v textu nebo třeba měnit formát data.
Regulární výraz, neboli regex, je textový řetězec složený z určitých znaků. Gramatika regulárních výrazů není složitá, ale je poměrně nepřehledná, a proto je dobré již napsané výrazy komentovat.
Na úvod si ukážeme příklad regulárního výrazu:
[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}
Cílem tohoto regulárního výrazu je zjednodušeně zjistit, zda-li je vložený textový řetězec emailem.
Výraz je dost zjednodušený, takže některé neplatné adresy jím projdou.
Zápis regulárních výrazů
Nyní se seznámíme s kvantifikátory a zástupnými znaky. Uvedeme si také nějaké další konstrukce.
Kvantifikátory
Kvantifikátory nám říkají, kolikrát se budou znaky opakovat.
Kvantifikátorů je několik typů, například:
{X}
, kdeX
udává počet opakování,{X, Y}
, kdeX `je minimální a `Y
maximální počet opakování,- předdefinované kvantifikátory.
Příklad čtyř libovolných znaků bychom tedy zapsali například takto:
^.{4}$
Vypišme si kvantifikátory do tabulky:
Znak | Význam |
---|---|
. | jeden libovolný znak |
* | žádný nebo více znaků |
+ | jeden nebo více znaků |
? | žádný nebo jeden znak |
{X} | X znaků |
{X,} | X a více znaků |
{X,Y} | Mezi X a Y znaky |
Složené závorky nám seskupují určitou část výrazu. Kvantifikátory se vztahují na celý obsah závorky.
Otazník ?
je alternativou k {0, 1}
. Hvězdička
*
k {0-∞}
a plus +
k
{1-∞}
. U předdefinované hvězdičky a pluska to funguje pro
maximálně nekonečno.
Ukažme si funkcionalitu kvantifikátoru .
, který nahrazuje
libovolný znak. Například pro výraz ....
bude
platit cokoliv, co má čtyři znaky:
Cdf
= neplatné
Ahoj
= platné
A@x9
= platné
A@x9O
= platné
Jak je možné, že výraz A@x9O
obsahující pět
znaků je platný pro výraz ....
? Ukažme si
postup vyhodnocení:
- První znak je tečka – výraz zatím splněn.
- Druhý znak je tečka – výraz zatím splněn.
- Třetí znak je tečka – výraz zatím splněn.
- Čtvrtý znak je tečka – výraz zatím splněn.
- Žádný další znak ve výrazu, výraz byl splněn.
Pokud řetězec obsahuje něco navíc,
projde stejně. Řešení tohoto problému je však snadné. Jednoduše před
výraz přidáme stříšku ^
( AltGr +
9 + 4), která zajistí, že na začátku řetězce bude
testovaný text. Za výraz dáme dolar $
(AltGr + ů).
Pokud je naším cílem zajistit, že celý řetězec má být přesně
čtyři znaky dlouhý, měli bychom použit výraz ^....$
.
Metaznak $
zajišťuje ověření od konce
řetězce. Tedy od konce ověříme, jestli řetězec splňuje pravidlo
odzadu i odpředu. V závorkách se již pravidlo ověřuje běžným směrem.
Znaky ^
a $
si probereme níže v dalších
konstrukcích.
Zástupné znaky
Zástupné znaky zkracují výraz a nahrazují nějaký znak nebo konstrukci:
Znak | Význam |
---|---|
\t | tabulátor |
\n | nový řádek |
\b | začátek nebo konec slova |
\d | číslice |
\D | znak, který není číslicí |
\w | písmena a číslice včetně podtržítka |
\W | znak, který není písmeno, číslice včetně podtržítka |
\B | pozice, , které není na začátku ani na konci slova |
\s | neviditelný znak |
\S | znak, který není neviditelný znak |
\\ | zpětné lomítko |
\d
jsou čísla 0-9
, tedy výraz je totožný s
výrazem [0-9]
. Výraz \D
je totožný s výrazem
[^0-9]
.
Další konstrukce
Nakonec se podíváme na některé další konstrukce:
Znak | Význam |
---|---|
abc | řetězec abc |
[abc] | jeden ze znaků a, b, c |
[^abc] | jeden znak kromě a, b, c (negace) |
[a-z] | malá písmena |
[A-Z] | velká písmena |
[^A-Za-z0-9] | symbol (cokoliv kromě písmena a čísla) |
^abc | abc na začátku řetězce |
abc$ | abc na konci řetězce |
^abc$ | celý řetězec musí být abc |
Stříška ^
označuje začátek řetězce. Dolar $
pak jeho konec.
Hranaté závorky ukazují na skupinu znaků, které
řetězec smí nebo nesmí obsahovat. Pokud je smí obsahovat, tak je jednoduše
napíšeme do závorky (ničím je neoddělujeme). Pokud je naopak nesmí
obsahovat, přidáme před znaky ještě stříšku
^
(Alt + 9 + 4).
Pokud chceme určit, že se má ověřovat třeba abeceda,
tak uvedeme [a-zA-z]
. Tímto zajistíme, že se zkontrolují
všechny znaky, které jsou mezi a-z
a A-Z
. Znaky se
berou z ASCII tabulky, takže třeba č
už daný výraz
nesplní.
Escapování
Někdy potřebujeme ve výrazu použít nějaký metaznak.
Třeba chceme ověřit, jestli uživatel zadal ahoj|světe
.
Jednotlivé speciální znaky musíme odescapovat, tedy
předsadit zpětným lomítkem (AltGr + Q). Výraz pak
bude vypadat následovně:
\(ahoj\|světe\)
Použití regexů v praxi
Pojďme si nyní ukázat použití regulárních výrazů s modulem re ze standardní knihovny Pythonu.
Validace
Validace je jedním ze základních způsobů použití regexů. V podstatě
se ptáme, jestli řetězec odpovídá zadanému předpisu. Využijeme k tomu
funkci re.match()
. Funkce je součástí modulu re v Pythonu a
slouží k porovnání zadaného řetězce s vzorcem regulárního výrazu. Tato
funkce se pokusí nalézt shodu na začátku zadaného řetězce a vrátí
objekt Match
, pokud je shoda nalezena, nebo None
,
pokud shoda nalezena není.
Syntaxe funkce re.match()
je následující:
re.match(pattern, string, flags=0)
Parametry funkce jsou následující:
pattern
- regulární výraz, se kterým chceme porovnat zadaný řetězec,string
- řetězec, který chceme porovnat s vzorcem,flags
- volitelný parametr, který určuje možnosti pro hledání shod (například ignorování velikosti písmen a podobně).
Podívejme se na příklad krátkého skriptu v Pythonu, který používá regulární výrazy k validaci e-mailové adresy:
import re # definujeme vzorec pro e-mailovou adresu pattern = r'^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$' # zadáme e-mailovou adresu, kterou chceme ověřit email = '[email protected]' # ověříme, zda e-mailová adresa odpovídá vzorci if re.match(pattern, email): print('E-mailová adresa je platná.') else: print('E-mailová adresa není platná.')
Ve výstupu vidíme:
Výstup funkce match():
E-mailová adresa je platná.
V tomto příkladu definujeme vzorec pro e-mailovou adresu pomocí
regulárního výrazu a poté zadáme e-mailovou adresu, kterou chceme ověřit.
Pomocí funkce re.match()
ověříme, zda zadaná e-mailová adresa
odpovídá vzorci.
Regex vždy formátujeme jako raw string
r""
. Tím zajistíme, že speciální sekvence jako např.
\n
nebudou plnit svůj speciální význam.
Validovat lze cokoliv - zejména formáty vstupů uživatelů. Tedy e-mailové adresy, hesla, URL, formuláře a podobně.
Vyhledávání
Pomocí funkce re.search()
vyhledáváme první část
řetězce, která odpovídá regulárnímu výrazu. Všechny odpovídající
podřetězce najdeme pomocí funkce re.findall()
a jednotlivé
shody pomocí re.finditer()
.
Následující příklad analyzuje výskyt slova Ahoj
v
textu:
import re text = "Ahoj, jak se máš? Já se mám moc dobře. Ahoj!" # Vyhledání první shody s regulárním výrazem match = re.search(r'Ahoj', text) if match: print("První shoda nalezena na indexu:", match.start()) # Vyhledání všech shod s regulárním výrazem matches = re.findall(r'Ahoj', text) print("Všechny shody:", matches) # Vyhledání všech shod s regulárním výrazem a vytištění jejich indexů for match in re.finditer(r'Ahoj', text): print("Shoda nalezena na indexu:", match.start())
Ve výstupu vidíme:
Výstup vyhledávání:
První shoda nalezena na indexu: 0
Všechny shody: ['Ahoj', 'Ahoj']
Shoda nalezena na indexu: 0
Shoda nalezena na indexu: 39
V tomto příkladu vyhledáváme slovo Ahoj
v textu pomocí
regulárního výrazu r'Ahoj'
. Funkce re.search()
najde první shodu a vrátí objekt Match
, který obsahuje
informace o shodě. Funkce re.findall()
vrátí seznam všech shod
a funkce re.finditer()
vrátí iterovatelný objekt, který
umožňuje procházet všechny shody v řetězci. V tomto případě vypisujeme
indexy všech shod v řetězci.
Nahrazování
Nahrazení části řetězce provedeme pomocí funkce re.sub()
.
Nahrazená část musí odpovídat regulárnímu výrazu. Podívejme se na
příklad:
import re text = "Ahoj, jak se máš? Já se mám moc dobře. Ahoj!" # nahrazení slova "Ahoj" slovem "Nazdar" vystup = re.sub(r'\bAhoj\b', 'Nazdar', text) print(vystup)
Ve výstupu vidíme:
Výstup nahrazení:
Nazdar, jak se máš? Já se mám moc dobře. Nazdar!
Nastavení chování
Regulární výrazy lze upravit pomocí tzv. flagů, které určují jejich
chování. Pokud potřebujeme použít více flagů najednou, řetězíme je
pomocí operátoru |
. Mezi nejčastěji používané patří:
re.IGNORECASE
- ignoruje velikost písmen,re.DOTALL
- dot (tečka) odpovídá i značkám konce řádku,re.MULTILINE
- hranice řádků jsou definovány značkami začátku a konce řádku,re.VERBOSE
- umožňuje psát regulární výrazy ve více řádcích a s komentáři,re.DEBUG
- zobrazí ladící hlášky týkající se zpracování regulárního výrazu.
Další flagy nalezneme v dokumentaci
Funkce re.compile()
Než se podíváme na příklad s flagy, vysvětlíme si ještě funkci
re.compile()
. Funkce z modulu re kompiluje regulární výraz do
objektu třídy SRE_Pattern
. Tento objekt používáme pro
vyhledávání, nahrazování nebo rozdělení textu pomocí regulárního
výrazu.
Funkce re.compile()
má následující syntaxi:
re.compile(pattern, flags=0)
Parametr pattern
je regulární výraz, který chceme
zkompilovat. Parametr flags
(viz výše) je volitelný.
Podívejme se na příklad:
import re text = """ Ahoj, jak se máš? Já se mám moc dobře. Nevolal jsem o pomoc. Ahoj! """ # Vytvoření regulárního výrazu pro hledání slova "moc" vzor = re.compile(r""" \bmoc\b # hledá slovo "moc" s ohledem na hranice slov """, re.IGNORECASE | re.VERBOSE) # Získání všech shod ve vstupním textu shody = vzor.findall(text, re.MULTILINE | re.DOTALL) # Výpis všech shod print(shody)
Ve výstupu vidíme:
Použití flagů:
['moc']
V tomto příkladu použijeme flagy re.IGNORECASE
,
re.VERBOSE
, re.MULTILINE
a re.DOTALL
.
Regulární výraz hledá slovo moc
s ohledem na hranice slov
(\bmoc\b
). Funkce re.compile()
slouží k vytvoření
regulárního výrazu, který lze použít opakovaně. Funkce
findall()
vrátí všechny shody ve vstupním textu. Flag
re.MULTILINE
určuje, že hranice řádků jsou definovány
značkami začátku a konce řádku, a re.DOTALL
určuje, že dot
(tečka) odpovídá i značkám konce řádku.
V následujícím kvízu, Kvíz - Iterátory, generátory a regexy v Pythonu, si vyzkoušíme nabyté zkušenosti z předchozích lekcí.