NOVINKA! E-learningové kurzy umělé inteligence. Nyní AI za nejlepší ceny. Zjisti více:
NOVINKA – Víkendový online kurz Software tester, který tě posune dál. Zjisti, jak na to!

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}, kde X udává počet opakování,
  • {X, Y}, kde X `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í:

  1. První znak je tečka – výraz zatím splněn.
  2. Druhý znak je tečka – výraz zatím splněn.
  3. Třetí znak je tečka – výraz zatím splněn.
  4. Čtvrtý znak je tečka – výraz zatím splněn.
  5. Žá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 + 94), 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 + 94).

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


 

Předchozí článek
Iterátory podruhé: Generátory v Pythonu
Všechny články v sekci
Kolekce v Pythonu
Přeskočit článek
(nedoporučujeme)
Kvíz - Iterátory, generátory a regexy v Pythonu
Článek pro vás napsal Karel Zaoral
Avatar
Uživatelské hodnocení:
99 hlasů
Karel Zaoral
Aktivity