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ů.
- re — Regular expression operations — Python 3.10.0 Documentation
- Regular Expression HOWTO — Python 3.10.0 Documentation
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ů
- Omezeno na znaky ASCII:
- Chamtivé a nechamtivé zápasy
- Sestavení vzoru regulárního výrazu: compile()
- shodný objekt
- Kontrola shody začátku řetězce, extract: match()
- Kontrola shody neomezené na začátek, výpis: search()
- Kontrola shody celého řetězce: fullmatch()
- Získání seznamu všech odpovídajících částí: findall()
- Získání všech odpovídajících částí jako iterátor: finditer()
- Nahraďte odpovídající části: sub(), subn()
- Dělení řetězců pomocí vzorů regulárních výrazů: split()
- Metaznaky regulárních výrazů, speciální sekvence a výhrady v jazyce Python
- Nastavení vlajky
- 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.
\1 | První závorka |
\2 | Druhá závorka |
\3 | Tř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í.
metaznak | obsah |
---|---|
. | 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 ~n opakování |
[] | Soubor znaků[] Odpovídá některému z těchto znaků |
| | NEBOA|B Odpovídá vzoru A nebo B |
speciální sekvence | obsah |
---|---|
\d | Desítková čísla Unicode (omezená na čísla ASCII příznakem ASCII) |
\D | \d Což znamená opak. |
\s | bílé znaky Unicode (omezené na bílé znaky ASCII příznakem ASCII) |
\S | \s Což znamená opak. |
\w | Slovní znaky Unicode a podtržítka (omezeno na alfanumerické znaky ASCII a podtržítka příznakem ASCII) |
\W | \w Což 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
\w
Ve 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.IGNORECASE
Pokud 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)
- zkratka
re.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.MULTILINE
Pokud 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)
- zkratka
re.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.