Ještě jednou a lépe: složitější regulární výrazy v Pythonu

Od Petr Zemek, 2014-05-28

Dnes se podíváme na to, jak přehledněji zapisovat a používat složitější regulární výrazy v Pythonu.

Původní kód

Mějme následující regulární výraz popisující datum, které může být zapsáno s různými oddělovači:

import re
 
date_re = re.compile(r'((?:19|20)\d\d)([- /.])(0[1-9]|1[012])\2(0[1-9]|[12][0-9]|3[01])')

Kód jsem s úpravou převzal odtud. Použití je následovné:

m = date_re.search('Today is 2014-05-28.')
print('Year:', m.group(1))  # Year: 2014
print('Month:', m.group(3)) # Month: 05
print('Day:', m.group(4))   # Day: 28

Proč takto raději ne?

Ač regulární výraz a kód výše funguje, je špatně čitelný a udržovatelný. V případě, že bychom jej potřebovali rozšířit či opravit, je potřeba dávat pozor, kde co zapisujeme, protože závorkami se to tam jen hemží. Taktéž z výrazu není ihned jasné, co která skupina znamená. V neposlední řadě se je potřeba na výsledné skupiny odkazovat číslem, což je náchylné na chyby, pokud změníme pořadí skupin.

Jak to udělat lépe?

Využijeme možnost zapisovat regulární výraz na více řádků použitím příznaku re.VERBOSE:

date_re = re.compile(r'''
    ((?:19|20)\d\d)           # year               (1)
    ([- /.])                  # delimiter          (2)
    (0[1-9]|1[012])           # month              (3)
    \2                        # the same delimiter
    (0[1-9]|[12][0-9]|3[01])  # day                (4)
''', re.VERBOSE)

Lze tak jednotlivé části regulárního výrazu přehledně strukturovat a do komentářů zmínit, co znamenají. Python při použití re.VERBOSE bílé znaky a komentáře ignoruje. Pokud byste chtěli zapsat mezeru či znak uvozující komentář, je potřeba před ně dát zpětné lomítko.

Pořád nám to ale neřeší problém s tím, že bychom se chtěli na skupiny odkazovat způsobem, který je méně náchylný na změny v regulárním výrazu. Tento problém lze vyřešit použitím pojmenovaných skupin:

date_re = re.compile(r'''
    (?P<year>(?:19|20)\d\d)
    (?P<delimiter>[- /.])
    (?P<month>0[1-9]|1[012])
    (?P=delimiter)
    (?P<day>0[1-9]|[12][0-9]|3[01])
''', re.VERBOSE)

Použití je pak následující:

m = date_re.search('Today is 2014-05-28.')
print('Year:', m.group('year'))   # Year: 2014
print('Month:', m.group('month')) # Month: 05
print('Day:', m.group('day'))     # Day: 28

No není to paráda? Vhodným pojmenováním skupin lze navíc docílit toho, že můžeme bez újmy na čitelnosti zrušit ony komentáře. Samozřejmě je potřeba, aby čtenář znal syntaxi regulárním výrazů v Pythonu. Ta je ale přehledně popsána v dokumentaci, takže žádný problém.

Obsah tohoto pole je soukromý a nebude veřejně zobrazen.

Filtrované HTML (využíváno)

  • Povolené HTML značky: <a href hreflang> <em> <strong> <cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <table>
  • Zvýraznění syntaxe kódu lze povolit přes následující značky: <code>, <blockcode>, <bash>, <c>, <cpp>, <haskell>, <html>, <java>, <javascript>, <latex>, <perl>, <php>, <python>, <ruby>, <rust>, <sql>, <text>, <vim>, <xml>, <yaml>.
  • Řádky a odstavce se zalomí automaticky.
  • Webové a e-mailové adresy jsou automaticky převedeny na odkazy.
CAPTCHA
6 + 12 =
Vyřešte tento jednoduchý matematický příklad a vložte výsledek. Např. pro 1+3 vložte 4.
Nějak se mi tady rozmohl spam, takže poprosím o ověření.