Jak používat modul regulárních výrazů Pythonu re (match, search, sub atd.)

Podnikání

Pro zpracování regulárních výrazů v jazyce Python používáme modul re ze standardní knihovny. Ten umožňuje extrahovat, nahrazovat a dělit řetězce pomocí vzorů regulárních výrazů.

V této části nejprve vysvětlíme funkce a metody modulu re.

  • Sestavování vzorů regulárních výrazů:compile()
  • shodný objekt
  • Zkontrolujte, zda začátek řetězce odpovídá, extrahujte:match()
  • Zkontrolujte shody, které nejsou omezeny na začátek:search()
  • Zkontroluje, zda se shoduje celý řetězec:fullmatch()
  • Získejte seznam všech odpovídajících dílů:findall()
  • Získat všechny odpovídající části jako iterátor:finditer()
  • Vyměňte odpovídající díl:sub(),subn()
  • Dělení řetězců pomocí vzorů regulárních výrazů:split()

Poté vysvětlím metaznaky (speciální znaky) a speciální sekvence regulárních výrazů, které lze použít v modulu re. V podstatě se jedná o standardní syntaxi regulárních výrazů, ale pozor na nastavení příznaků (zejména re.ASCII).

  • Metaznaky regulárních výrazů, speciální sekvence a výhrady v jazyce Python
  • Nastavení vlajky
    • Omezeno na znaky ASCII:re.ASCII
    • Nerozlišuje velká a malá písmena:re.IGNORECASE
    • Shodujte začátek a konec každého řádku:re.MULTILINE
    • Zadání více příznaků
  • Chamtivé a nechamtivé zápasy

Sestavení vzoru regulárního výrazu: compile()

Zpracování regulárních výrazů v modulu re lze provádět dvěma způsoby.

Spustit s funkcí

První je funkce.re.match(),re.sub()Tyto funkce jsou k dispozici pro provádění extrakce, nahrazování a dalších procesů pomocí vzorů regulárních výrazů.

Podrobnosti o funkcích budou popsány později, ale ve všech je prvním argumentem řetězec vzoru regulárního výrazu, následuje řetězec, který se má zpracovat, a tak dále. Například u funkce re.sub(), která provádí substituci, je druhým argumentem substituční řetězec a třetím argumentem je řetězec, který má být zpracován.

import re

s = 'aaa@xxx.com, bbb@yyy.com, ccc@zzz.net'

m = re.match(r'([a-z]+)@([a-z]+)\.com', s)
print(m)
# <re.Match object; span=(0, 11), match='aaa@xxx.com'>

result = re.sub(r'([a-z]+)@([a-z]+)\.com', 'new-address', s)
print(result)
# new-address, new-address, ccc@zzz.net

Všimněte si, že [a-z] ve vzoru regulárního výrazu v tomto příkladu znamená libovolný znak od a do z (tj. malá písmena abecedy) a + znamená opakování předchozího vzoru (v tomto případě [a-z]) jeden nebo vícekrát. Vzor [a-z]+ odpovídá jakémukoli řetězci, který opakuje jeden nebo více znaků malé abecedy.

. je metaznak (znak se zvláštním významem) a musí být escapován zpětným lomítkem.

Protože řetězce vzorů regulárních výrazů často používají mnoho zpětných lomítek, je vhodné používat nezpracované řetězce jako v příkladu.

Probíhá v metodě objektu vzoru regulárního výrazu

Druhým způsobem zpracování regulárních výrazů v modulu re je objektová metoda regulárních výrazů.

Pomocí funkce re.compile() můžete zkompilovat řetězec vzoru regulárního výrazu a vytvořit objekt vzoru regulárního výrazu.

p = re.compile(r'([a-z]+)@([a-z]+)\.com')

print(p)
# re.compile('([a-z]+)@([a-z]+)\\.com')

print(type(p))
# <class 're.Pattern'>

re.match(),re.sub()Stejný postup jako tyto funkce lze provést například jako metody match(),sub() objektů regulárních výrazů.

m = p.match(s)
print(m)
# <re.Match object; span=(0, 11), match='aaa@xxx.com'>

result = p.sub('new-address', s)
print(result)
# new-address, new-address, ccc@zzz.net

Všechny níže popsané funkce re.xxx() jsou k dispozici také jako metody objektu regulárního výrazu.

Pokud opakujete proces, který používá stejný vzor, je efektivnější vygenerovat objekt regulárního výrazu pomocí re.compile() a použít jej v okolí.

V následující ukázce kódu je funkce pro pohodlí použita bez kompilace, ale pokud chcete stejný vzor používat opakovaně, doporučujeme ji předem zkompilovat a spustit jako metodu objektu regulárního výrazu.

shodný objekt

match(), search() atd. vrací objekt match.

s = 'aaa@xxx.com'

m = re.match(r'[a-z]+@[a-z]+\.[a-z]+', s)
print(m)
# <re.Match object; span=(0, 11), match='aaa@xxx.com'>

print(type(m))
# <class 're.Match'>

Shodný řetězec a pozice jsou získány pomocí následujících metod objektu match.

  • Získejte umístění zápasu:start(),end(),span()
  • Získat odpovídající řetězec:group()
  • Získejte řetězec pro každou skupinu:groups()
print(m.start())
# 0

print(m.end())
# 11

print(m.span())
# (0, 11)

print(m.group())
# aaa@xxx.com

Pokud část vzoru regulárního výrazu uzavřete do řetězce se závorkami(), bude tato část zpracována jako skupina. V takovém případě lze řetězec části, která odpovídá každé skupině v groups(), získat jako tuple.

m = re.match(r'([a-z]+)@([a-z]+)\.([a-z]+)', s)
print(m)
# <re.Match object; span=(0, 11), match='aaa@xxx.com'>

print(m.groups())
# ('aaa', 'xxx', 'com')

Kontrola shody začátku řetězce, extract: match()

match() vrátí objekt match, pokud začátek řetězce odpovídá vzoru.

Jak bylo uvedeno výše, objekt match lze použít k extrakci odpovídajícího podřetězce nebo jednoduše ke kontrole, zda došlo ke shodě.

match() zkontroluje pouze začátek. Pokud na začátku není žádný odpovídající řetězec, vrátí None.

s = 'aaa@xxx.com, bbb@yyy.com, ccc@zzz.net'

m = re.match(r'[a-z]+@[a-z]+\.com', s)
print(m)
# <re.Match object; span=(0, 11), match='aaa@xxx.com'>

m = re.match(r'[a-z]+@[a-z]+\.net', s)
print(m)
# None

Kontrola shody neomezené na začátek, výpis: search()

Stejně jako funkce match() vrací objekt match, pokud se shoduje.

Pokud existuje více shodných částí, bude vrácena pouze první shodná část.

s = 'aaa@xxx.com, bbb@yyy.com, ccc@zzz.net'

m = re.search(r'[a-z]+@[a-z]+\.net', s)
print(m)
# <re.Match object; span=(26, 37), match='ccc@zzz.net'>

m = re.search(r'[a-z]+@[a-z]+\.com', s)
print(m)
# <re.Match object; span=(0, 11), match='aaa@xxx.com'>

Pokud chcete získat všechny odpovídající části, použijte funkci findall() nebo finditer(), jak je popsáno níže.

Kontrola shody celého řetězce: fullmatch()

Chcete-li zkontrolovat, zda celý řetězec odpovídá vzoru regulárního výrazu, použijte funkci fullmatch(). To je užitečné například pro kontrolu, zda je řetězec platný jako e-mailová adresa, nebo ne.

Pokud se shoduje celý řetězec, je vrácen objekt shody.

s = 'aaa@xxx.com'

m = re.fullmatch(r'[a-z]+@[a-z]+\.com', s)
print(m)
# <re.Match object; span=(0, 11), match='aaa@xxx.com'>

Pokud existují neshodné části (pouze částečná shoda nebo žádná shoda), je vrácena hodnota None.

s = '!!!aaa@xxx.com!!!'

m = re.fullmatch(r'[a-z]+@[a-z]+\.com', s)
print(m)
# None

Funkce fullmatch() byla přidána v jazyce Python 3.4. Pokud chcete totéž provést v dřívějších verzích, použijte match() a odpovídající metaznak $ na konci. Pokud se celý řetězec od začátku do konce neshoduje, vrátí funkci None.

s = '!!!aaa@xxx.com!!!'

m = re.match(r'[a-z]+@[a-z]+\.com$', s)
print(m)
# None

Získání seznamu všech odpovídajících částí: findall()

funkce findall() vrátí seznam všech odpovídajících podřetězců. Všimněte si, že prvky seznamu nejsou objekty shody, ale řetězce.

s = 'aaa@xxx.com, bbb@yyy.com, ccc@zzz.net'

result = re.findall(r'[a-z]+@[a-z]+\.[a-z]+', s)
print(result)
# ['aaa@xxx.com', 'bbb@yyy.com', 'ccc@zzz.net']

Počet odpovídajících částí lze zjistit pomocí vestavěné funkce len(), která vrací počet prvků v seznamu.

print(len(result))
# 3

Seskupení pomocí závorek() ve vzoru regulárního výrazu vrátí seznam tuplů, jejichž prvky jsou řetězce jednotlivých skupin. To je ekvivalentní funkci groups() v objektu match.

result = re.findall(r'([a-z]+)@([a-z]+)\.([a-z]+)', s)
print(result)
# [('aaa', 'xxx', 'com'), ('bbb', 'yyy', 'com'), ('ccc', 'zzz', 'net')]

Skupinové závorky () mohou být vnořené, takže pokud chcete získat i celou shodu, stačí uzavřít celou shodu do závorek ().

result = re.findall(r'(([a-z]+)@([a-z]+)\.([a-z]+))', s)
print(result)
# [('aaa@xxx.com', 'aaa', 'xxx', 'com'), ('bbb@yyy.com', 'bbb', 'yyy', 'com'), ('ccc@zzz.net', 'ccc', 'zzz', 'net')]

Pokud není nalezena žádná shoda, je vrácen prázdný tuple.

result = re.findall('[0-9]+', s)
print(result)
# []

Získání všech odpovídajících částí jako iterátor: finditer()

funkce finditer() vrací všechny odpovídající části jako iterátor. Prvky nejsou řetězce jako v případě funkce findall(), ale shodné objekty, takže můžete získat pozici (index) shodných částí.

Samotný iterátor nelze vypsat pomocí print() a získat tak jeho obsah. Pokud použijete vestavěnou funkci next() nebo příkaz for, můžete získat obsah postupně.

s = 'aaa@xxx.com, bbb@yyy.com, ccc@zzz.net'

result = re.finditer(r'[a-z]+@[a-z]+\.[a-z]+', s)
print(result)
# <callable_iterator object at 0x10b0efa90>

print(type(result))
# <class 'callable_iterator'>

for m in result:
    print(m)
# <re.Match object; span=(0, 11), match='aaa@xxx.com'>
# <re.Match object; span=(13, 24), match='bbb@yyy.com'>
# <re.Match object; span=(26, 37), match='ccc@zzz.net'>

Lze jej také převést na seznam pomocí funkce list().

l = list(re.finditer(r'[a-z]+@[a-z]+\.[a-z]+', s))
print(l)
# [<re.Match object; span=(0, 11), match='aaa@xxx.com'>, <re.Match object; span=(13, 24), match='bbb@yyy.com'>, <re.Match object; span=(26, 37), match='ccc@zzz.net'>]

print(l[0])
# <re.Match object; span=(0, 11), match='aaa@xxx.com'>

print(type(l[0]))
# <class 're.Match'>

print(l[0].span())
# (0, 11)

Pokud chcete získat pozici všech odpovídajících částí, je zápis s porozuměním seznamu výhodnější než list().

print([m.span() for m in re.finditer(r'[a-z]+@[a-z]+\.[a-z]+', s)])
# [(0, 11), (13, 24), (26, 37)]

Iterátor vybírá prvky v pořadí. Všimněte si, že pokud se po dosažení konce pokusíte vyjmout další prvky, nezůstane vám nic.

result = re.finditer(r'[a-z]+@[a-z]+\.[a-z]+', s)

for m in result:
    print(m)
# <re.Match object; span=(0, 11), match='aaa@xxx.com'>
# <re.Match object; span=(13, 24), match='bbb@yyy.com'>
# <re.Match object; span=(26, 37), match='ccc@zzz.net'>

print(list(result))
# []

Nahraďte odpovídající části: sub(), subn()

Pomocí funkce sub() můžete nahrazovat odpovídající část jiným řetězcem. Vrátí se nahrazený řetězec.

s = 'aaa@xxx.com, bbb@yyy.com, ccc@zzz.net'

result = re.sub(r'[a-z]+@[a-z]+\.com', 'new-address', s)
print(result)
# new-address, new-address, ccc@zzz.net

print(type(result))
# <class 'str'>

Při seskupování pomocí závorek() lze v nahrazovaném řetězci použít přiřazený řetězec.

Ve výchozím nastavení jsou podporovány následující možnosti: Všimněte si, že u normálních řetězců, které nejsou surovými řetězci, musí být před zpětným lomítkem uvedeno zpětné lomítko, aby se zpětné lomítko uniklo.

\1První závorka
\2Druhá závorka
\3Třetí závorka
result = re.sub(r'([a-z]+)@([a-z]+)\.com', r'\1@\2.net', s)
print(result)
# aaa@xxx.net, bbb@yyy.net, ccc@zzz.net

?P<xxx>
Pokud skupinu pojmenujete tak, že ji napíšete na začátek závorek vzoru regulárního výrazu, můžete ji zadat pomocí názvu místo čísla, jak je uvedeno níže.
\g<xxx>

result = re.sub(r'(?P<local>[a-z]+)@(?P<SLD>[a-z]+)\.com', r'\g<local>@\g<SLD>.net', s)
print(result)
# aaa@xxx.net, bbb@yyy.net, ccc@zzz.net

Argument count určuje maximální počet nahrazení. Nahrazen bude pouze počet z levé strany.

result = re.sub(r'[a-z]+@[a-z]+\.com', 'new-address', s, count=1)
print(result)
# new-address, bbb@yyy.com, ccc@zzz.net

subn() vrací tuple substituovaného řetězce (stejný jako návratová hodnota sub()) a počet substituovaných částí (počet částí, které odpovídají vzoru).

result = re.subn(r'[a-z]+@[a-z]+\.com', 'new-address', s)
print(result)
# ('new-address, new-address, ccc@zzz.net', 2)

Způsob zadávání argumentů je stejný jako u funkce sub(). Můžete použít část seskupenou do závorek nebo zadat počet argumentů.

result = re.subn(r'(?P<local>[a-z]+)@(?P<SLD>[a-z]+)\.com', r'\g<local>@\g<SLD>.net', s)
print(result)
# ('aaa@xxx.net, bbb@yyy.net, ccc@zzz.net', 2)

result = re.subn(r'[a-z]+@[a-z]+\.com', 'new-address', s, count=1)
print(result)
# ('new-address, bbb@yyy.com, ccc@zzz.net', 1)

Dělení řetězců pomocí vzorů regulárních výrazů: split()

split() rozdělí řetězec v části, která odpovídá vzoru, a vrátí jej jako seznam.

Všimněte si, že první a poslední shoda bude obsahovat prázdné řetězce na začátku a na konci výsledného seznamu.

s = '111aaa222bbb333'

result = re.split('[a-z]+', s)
print(result)
# ['111', '222', '333']

result = re.split('[0-9]+', s)
print(result)
# ['', 'aaa', 'bbb', '']

Argument maxsplit určuje maximální počet rozdělení (kusů). Rozdělí se pouze počet z levé strany.

result = re.split('[a-z]+', s, 1)
print(result)
# ['111', '222bbb333']

Metaznaky regulárních výrazů, speciální sekvence a výhrady v jazyce Python

Hlavní metaznaky regulárních výrazů (speciální znaky) a speciální sekvence, které lze použít v modulu Python 3 re, jsou následující.

metaznakobsah
.Jakýkoli jednotlivý znak jiný než nový řádek (včetně nového řádku s příznakem DOTALL).
^Začátek řetězce (odpovídá také začátku každého řádku s příznakem MULTILINE).
$Konec řetězce (odpovídá také konci každého řádku s příznakem MULTILINE).
*Opakování předchozího vzoru více než 0krát
+Zopakujte předchozí vzor alespoň jednou.
?Opakování předchozího vzoru 0 nebo 1krát
{m}Opakování předchozího vzoru m krát
{m, n}Poslední vzor.m~nopakování
[]Soubor znaků[]Odpovídá některému z těchto znaků
|NEBOA|BOdpovídá vzoru A nebo B
speciální sekvenceobsah
\dDesítková čísla Unicode (omezená na čísla ASCII příznakem ASCII)
\D\dCož znamená opak.
\sbílé znaky Unicode (omezené na bílé znaky ASCII příznakem ASCII)
\S\sCož znamená opak.
\wSlovní znaky Unicode a podtržítka (omezeno na alfanumerické znaky ASCII a podtržítka příznakem ASCII)
\W\wCož znamená opak.

V této tabulce nejsou uvedeny všechny. Kompletní seznam najdete v oficiální dokumentaci.

Všimněte si také, že některé významy jsou v jazyce Python 2 odlišné.

Nastavení vlajky

Jak je uvedeno v tabulce výše, některé metaznaky a speciální sekvence mění svůj režim v závislosti na příznaku.

Zde jsou zahrnuty pouze hlavní vlajky. Ostatní najdete v oficiální dokumentaci.

Omezeno na znaky ASCII: re.ASCII

\wVe výchozím nastavení pro řetězce Pythonu 3 to bude také odpovídat dvoubajtovým znakům kanji, alfanumerickým znakům atd. Není ekvivalentní následujícímu výrazu, protože se nejedná o standardní regulární výraz.[a-zA-Z0-9_]

m = re.match(r'\w+', '漢字ABC123')
print(m)
# <re.Match object; span=(0, 11), match='漢字ABC123'>

m = re.match('[a-zA-Z0-9_]+', '漢字ABC123')
print(m)
# None

Pokud v každé funkci zadáte příznaky argumentu re.ASCII nebo přidáte na začátek řetězce vzoru regulárního výrazu následující inline příznak, bude funkce porovnávat pouze znaky ASCII (nebude porovnávat dvoubajtové japonské znaky, alfanumerické znaky atd.).
(?a)
V tomto případě jsou následující dvě rovnocenné.
\w=[a-zA-Z0-9_]

m = re.match(r'\w+', '漢字ABC123', flags=re.ASCII)
print(m)
# None

m = re.match(r'(?a)\w+', '漢字ABC123')
print(m)
# None

Totéž platí při kompilaci pomocí re.compile(). Použijte argument flags nebo inline flags.

p = re.compile(r'\w+', flags=re.ASCII)
print(p)
# re.compile('\\w+', re.ASCII)

print(p.match('漢字ABC123'))
# None

p = re.compile(r'(?a)\w+')
print(p)
# re.compile('(?a)\\w+', re.ASCII)

print(p.match('漢字ABC123'))
# None

ASCII je k dispozici také jako zkrácená forma re. A. Můžete použít buď.

print(re.ASCII is re.A)
# True

\W, opak \W, je také ovlivněn příznaky re.ASCII a inline.

m = re.match(r'\W+', '漢字ABC123')
print(m)
# None

m = re.match(r'\W+', '漢字ABC123', flags=re.ASCII)
print(m)
# <re.Match object; span=(0, 11), match='漢字ABC123'>

Stejně jako u \w následující dva příkazy ve výchozím nastavení odpovídají jak jednobajtovým, tak dvoubajtovým znakům, ale jsou omezeny na jednobajtové znaky, pokud jsou zadány příznaky re.ASCII nebo inline.

  • Shodujte se s čísly\d
  • Odpovídá prázdnému místu\s
  • Shoduje se s čísly, která nejsou čísly\D
  • Shoduje se s jakýmkoli jiným znakem než mezerou.\S
m = re.match(r'\d+', '123')
print(m)
# <re.Match object; span=(0, 3), match='123'>

m = re.match(r'\d+', '123')
print(m)
# <re.Match object; span=(0, 3), match='123'>

m = re.match(r'\d+', '123', flags=re.ASCII)
print(m)
# <re.Match object; span=(0, 3), match='123'>

m = re.match(r'\d+', '123', flags=re.ASCII)
print(m)
# None

m = re.match(r'\s+', ' ')  # full-width space
print(m)
# <re.Match object; span=(0, 1), match='\u3000'>

m = re.match(r'\s+', ' ', flags=re.ASCII)
print(m)
# None

Nerozlišuje velká a malá písmena:re.IGNORECASE

Ve výchozím nastavení se rozlišují velká a malá písmena. Chcete-li porovnávat obě písmena, musíte do vzoru zahrnout jak velká, tak malá písmena.

re.IGNORECASEPokud je zadán, bude se porovnávat bez ohledu na velikost písmen. Ekvivalent příznaku i ve standardních regulárních výrazech.

m = re.match('[a-zA-Z]+', 'abcABC')
print(m)
# <re.Match object; span=(0, 6), match='abcABC'>

m = re.match('[a-z]+', 'abcABC', flags=re.IGNORECASE)
print(m)
# <re.Match object; span=(0, 6), match='abcABC'>

m = re.match('[A-Z]+', 'abcABC', flags=re.IGNORECASE)
print(m)
# <re.Match object; span=(0, 6), match='abcABC'>

Můžete použít hodnotu menší než nebo rovnou.

  • inline příznak(?i)
  • zkratkare.I

Shodujte začátek a konec každého řádku:re.MULTILINE

^Meta znaky v tomto regulárním výrazu odpovídají začátku řetězce.

Ve výchozím nastavení se porovnává pouze začátek celého řetězce, ale následující příkaz porovná i začátek každého řádku. Ekvivalent příznaku m ve standardních regulárních výrazech.
re.MULTILINE

s = '''aaa-xxx
bbb-yyy
ccc-zzz'''

print(s)
# aaa-xxx
# bbb-yyy
# ccc-zzz

result = re.findall('[a-z]+', s)
print(result)
# ['aaa', 'xxx', 'bbb', 'yyy', 'ccc', 'zzz']

result = re.findall('^[a-z]+', s)
print(result)
# ['aaa']

result = re.findall('^[a-z]+', s, flags=re.MULTILINE)
print(result)
# ['aaa', 'bbb', 'ccc']

$Shoduje se s koncem řetězce. Ve výchozím nastavení se porovnává pouze konec celého řetězce.
re.MULTILINEPokud zadáte tuto hodnotu, bude se shodovat i s koncem každého řádku.

result = re.findall('[a-z]+$', s)
print(result)
# ['zzz']

result = re.findall('[a-z]+$', s, flags=re.MULTILINE)
print(result)
# ['xxx', 'yyy', 'zzz']

Můžete použít hodnotu menší než nebo rovnou.

  • inline příznak(?m)
  • zkratkare.M

Zadání více příznaků

|Pokud chcete povolit více příznaků najednou, použijte tento příkaz. V případě inline příznaků musí za každým znakem následovat písmeno, jak je uvedeno níže.
(?am)

s = '''aaa-xxx
漢漢漢-字字字
bbb-zzz'''

print(s)
# aaa-xxx
# 漢漢漢-字字字
# bbb-zzz

result = re.findall(r'^\w+', s, flags=re.M)
print(result)
# ['aaa', '漢漢漢', 'bbb']

result = re.findall(r'^\w+', s, flags=re.M | re.A)
print(result)
# ['aaa', 'bbb']

result = re.findall(r'(?am)^\w+', s)
print(result)
# ['aaa', 'bbb']

Chamtivé a nechamtivé zápasy

To je obecný problém regulárních výrazů, nejen problém Pythonu, ale napíšu o něm, protože mě obvykle dostává do problémů.

Ve výchozím nastavení je následující shoda greedy match, která odpovídá nejdelšímu možnému řetězci.

  • *
  • +
  • ?
s = 'aaa@xxx.com, bbb@yyy.com'

m = re.match(r'.+com', s)
print(m)
# <re.Match object; span=(0, 24), match='aaa@xxx.com, bbb@yyy.com'>

print(m.group())
# aaa@xxx.com, bbb@yyy.com

Písmeno ? za ním bude mít za následek minimální shodu, která nebude šetrná a bude odpovídat nejkratšímu možnému řetězci.

  • *?
  • +?
  • ??
m = re.match(r'.+?com', s)
print(m)
# <re.Match object; span=(0, 11), match='aaa@xxx.com'>

print(m.group())
# aaa@xxx.com

Všimněte si, že výchozí greedy match může odpovídat neočekávaným řetězcům.

Copied title and URL