Hur man använder Pythons modul för reguljära uttryck re (match, search, sub, etc.)

Företag

För att bearbeta reguljära uttryck i Python använder vi re-modulen från standardbiblioteket. Den gör det möjligt att extrahera, ersätta och dela upp strängar med hjälp av reguljära uttrycksmönster.

I det här avsnittet förklarar vi först funktionerna och metoderna i re-modulen.

  • Sammanställning av mönster för reguljära uttryck:compile()
  • matchobjekt.
  • Kontrollera om början av strängen matchar, extrahera:match()
  • Kontrollera om det finns matchningar som inte är begränsade till början:search()
  • Kontrollera om hela strängen matchar:fullmatch()
  • Få en lista över alla matchande delar:findall()
  • Hämta alla matchande delar som en iterator:finditer()
  • Byt ut den matchande delen:sub(),subn()
  • Dela strängar med reguljära uttrycksmönster:split()

Därefter förklarar jag meta-tecken (specialtecken) och speciella sekvenser i reguljära uttryck som kan användas i re-modulen. I princip är det standard syntaxen för reguljära uttryck, men var försiktig med att sätta flaggor (särskilt re.ASCII).

  • Metatecken för reguljära uttryck, speciella sekvenser och förbehåll i Python
  • Ställa in flaggan
    • Begränsat till ASCII-tecken:re.ASCII
    • Inte skiftlägeskänslig:re.IGNORECASE
    • Matcha början och slutet på varje rad:re.MULTILINE
    • Ange flera flaggor
  • Giriga och icke-giriga matcher

Kompilera det reguljära uttrycksmönstret: compile()

Det finns två sätt att bearbeta reguljära uttryck i re-modulen.

Kör med funktion

Den första är en funktion.re.match(),re.sub()Funktioner som dessa är tillgängliga för att utföra extraktion, ersättning och andra processer med hjälp av reguljära uttrycksmönster.

Detaljerna för funktionerna beskrivs senare, men i alla funktioner är det första argumentet strängen i det reguljära uttrycksmönstret, följt av den sträng som ska bearbetas och så vidare. I re.sub(), som utför substitution, är till exempel det andra argumentet substitutionssträngen och det tredje argumentet den sträng som skall bearbetas.

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

Observera att [a-z] i det reguljära uttrycksmönstret i det här exemplet betyder alla tecken från a till z (dvs. alfabetet med små bokstäver), och + betyder att det tidigare mönstret (i det här fallet [a-z]) upprepas en eller flera gånger. [a-z]+ matchar alla strängar som upprepar ett eller flera alfabetiska tecken med små bokstäver.

. är ett metatecken (ett tecken med särskild betydelse) och måste undvikas med ett backslash.

Eftersom mönstersträngar för reguljära uttryck ofta använder många backslashes är det praktiskt att använda råa strängar som i exemplet.

Körs i en metod för ett objekt med reguljära uttrycksmönster.

Det andra sättet att bearbeta reguljära uttryck i re-modulen är objektmetoden för reguljära uttrycksmönster.

Med hjälp av re.compile() kan du kompilera en mönstersträng för reguljära uttryck för att skapa ett mönsterobjekt för reguljära uttryck.

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()Samma process som dessa funktioner kan till exempel utföras som metoder match(),sub() i reguljära uttrycksobjekt.

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

Alla re.xxx()-funktioner som beskrivs nedan finns också som metoder i objektet för reguljära uttryck.

Om du upprepar en process som använder samma mönster är det effektivare att generera ett reguljärt uttrycksobjekt med re.compile() och använda det runt omkring.

I följande exempelkod används funktionen utan kompilering för enkelhetens skull, men om du vill använda samma mönster upprepade gånger rekommenderas det att du kompilerar den i förväg och kör den som en metod i ett reguljärt uttrycksobjekt.

matchobjekt.

match(), search() osv. returnerar ett matchobjekt.

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'>

Den matchade strängen och positionen erhålls med hjälp av följande metoder för matchobjektet.

  • Hämta platsen för matchen:start(),end(),span()
  • Hämta den matchade strängen:group()
  • Hämta strängen för varje grupp:groups()
print(m.start())
# 0

print(m.end())
# 11

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

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

Om du omsluter en del av ett reguljärt uttrycksmönster i en sträng med parenteser() kommer delen att behandlas som en grupp. I det här fallet kan strängen för den del som matchar varje grupp i groups() erhållas som en tupel.

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')

Kontrollera om början av en sträng matchar, utdrag: match()

match() returnerar ett matchobjekt om början av strängen matchar mönstret.

Som nämnts ovan kan matchobjektet användas för att extrahera den matchade delsträngen eller helt enkelt för att kontrollera om en matchning har gjorts.

match() kontrollerar bara början. Om det inte finns någon matchande sträng i början returneras 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

Kontrollera om det finns fler träffar än början, utdrag: search()

Liksom match() returnerar den ett matchobjekt om den matchar.

Om det finns flera matchande delar kommer endast den första matchande delen att returneras.

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'>

Om du vill få fram alla matchande delar använder du findall() eller finditer() enligt beskrivningen nedan.

Kontrollera om hela strängen matchar: fullmatch()

Om du vill kontrollera om hela strängen matchar det reguljära uttrycksmönstret använder du fullmatch(). Detta är till exempel användbart för att kontrollera om en sträng är giltig som e-postadress eller inte.

Om hela strängen matchar återges ett matchobjekt.

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'>

Om det inte finns några matchade delar (endast partiella matchningar eller inga matchningar alls) returneras None.

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

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

Fullmatch() lades till i Python 3.4. Om du vill göra samma sak i tidigare versioner använder du match() och ett matchande metatecken $ i slutet. Om hela strängen från början till slut inte matchar, returneras None.

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

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

Få en lista över alla matchande delar: findall()

findall() returnerar en lista med alla matchande understrängar. Observera att listans element inte är matchobjekt utan strängar.

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']

Antalet matchade delar kan kontrolleras med hjälp av den inbyggda funktionen len(), som returnerar antalet element i listan.

print(len(result))
# 3

Gruppering med parenteser() i ett reguljärt uttrycksmönster returnerar en lista med tupler vars element är strängarna i varje grupp. Detta motsvarar groups() i matchobjektet.

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

Gruppparenteserna () kan vara inbäddade i varandra, så om du vill få fram hela matchningen också, omsluter du bara hela matchningen i parenteser ().

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')]

Om ingen träff hittas returneras en tom tupel.

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

Hämta alla matchande delar som en iterator: finditer()

finditer() returnerar alla matchande delar som en iterator. Elementen är inte strängar som i findall(), utan matchobjekt, så att du kan få positionen (index) för de matchade delarna.

Själva iteratorn kan inte skrivas ut med print() för att få fram dess innehåll. Om du använder den inbyggda funktionen next() eller for-angivelsen kan du få fram innehållet en efter en.

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'>

Den kan också omvandlas till en lista med 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)

Om du vill få fram positionen för alla matchande delar är list comprehension-notationen lämpligare än list().

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

Iteratorn tar ut element i ordning. Observera att om du försöker ta ut fler element efter att ha nått slutet kommer du inte att få något kvar.

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))
# []

Ersätt de delar som matchar varandra: sub(), subn()

Med sub() kan du ersätta den matchade delen med en annan sträng. Den ersatta strängen returneras.

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'>

Vid gruppering med parentes() kan den matchade strängen användas i den ersatta strängen.

Som standard stöds följande: Observera att för normala strängar som inte är råa strängar måste ett backslash anges före backslash för att undkomma backslash.

\1Den första parentesen
\2Den andra parentesen
\3Den tredje parentesen
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>
Om du namnger gruppen genom att skriva detta i början av det reguljära uttrycksmönstrets parenteser kan du ange den med hjälp av namnet i stället för numret, vilket visas nedan.
\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

Argumentet count anger det maximala antalet ersättningar. Endast antalet från vänster sida ersätts.

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() returnerar en tupel av den ersatta strängen (samma som returvärdet av sub()) och antalet ersatta delar (det antal som matchade mönstret).

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

Metoden för att ange argument är densamma som för sub(). Du kan använda den del som är grupperad inom parentes eller ange antalet 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)

Dela strängar med reguljära uttrycksmönster: split()

split() delar strängen i den del som matchar mönstret och returnerar den som en lista.

Observera att de första och sista träffarna kommer att innehålla tomma strängar i början och slutet av den resulterande listan.

s = '111aaa222bbb333'

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

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

Argumentet maxsplit anger det maximala antalet splits (delar). Endast antalet från vänster sida delas.

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

Metatecken för reguljära uttryck, speciella sekvenser och förbehåll i Python

De viktigaste metatecknen för reguljära uttryck (specialtecken) och specialsekvenser som kan användas i Python 3 re-modulen är följande

metateckeninnehåll
.Varje enskilt tecken utom en ny rad (inklusive en ny rad med flaggan DOTALL).
^Början av strängen (matchar även början av varje rad med flaggan MULTILINE).
$Slutet på strängen (matchar även slutet på varje rad med flaggan MULTILINE).
*Upprepa det föregående mönstret mer än 0 gånger
+Upprepa det föregående mönstret minst en gång.
?Upprepa det föregående mönstret 0 eller 1 gång
{m}Upprepa föregående mönster m gånger
{m, n}Det sista mönstret.m~nupprepa
[]En uppsättning tecken[]Matchar något av dessa tecken
|ELLERA|BMatchar antingen A eller B-mönstret
Särskild sekvens.innehåll
\dUnicode decimaltal (begränsat till ASCII-tal med ASCII-flaggan).
\D\dDet betyder motsatsen till detta.
\sUnicode-vitrymdstecken (begränsat till ASCII-vitrymdstecken med ASCII-flaggan).
\S\sDet betyder motsatsen till detta.
\wUnicode-ordtecken och understrykningar (begränsat till alfanumeriska ASCII-tecken och understrykningar genom ASCII-flagga).
\W\wDet betyder motsatsen till detta.

Alla är inte med i denna tabell. Se den officiella dokumentationen för en fullständig lista.

Observera också att vissa betydelser är annorlunda i Python 2.

Ställa in flaggan

Som framgår av tabellen ovan ändrar vissa metatecken och specialsekvenser sitt läge beroende på flaggan.

Endast de viktigaste flaggorna behandlas här. Se den officiella dokumentationen för resten.

Begränsat till ASCII-tecken: re.ASCII

\wDetta kommer också att matcha kanji med dubbla byte, alfanumeriska tecken etc. som standard för Python 3-strängar. Det är inte likvärdigt med följande eftersom det inte är ett standardregleruttryck.[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

Om du anger re.ASCII som argumentflaggor i varje funktion, eller lägger till följande inline-flagga i början av strängen för det reguljära uttrycksmönstret, kommer det endast att matcha ASCII-tecken (det kommer inte att matcha japanska dubbel-byte-tecken, alfanumeriska tecken, etc.).
(?a)
I detta fall är följande två likvärdiga.
\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

Samma sak gäller när du kompilerar med re.compile(). Använd argumentet flags eller 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 finns också som kortform re. A. Du kan använda antingen.

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

\W, motsatsen till \W, påverkas också av re.ASCII och inline-flaggor.

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'>

Precis som \w matchar följande två tecken både single-byte- och double-byte-te-tecken som standard, men begränsas till single-byte-te-tecken om re.ASCII- eller inline-flaggor har angetts.

  • Matcha siffrorna\d
  • Matchar ett tomt utrymme\s
  • Matchar icke-nummer\D
  • Matchar alla tecken som inte är mellanslag.\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

Inte skiftlägeskänslig:re.IGNORECASE

Som standard är den skiftlägeskänslig. Om du vill matcha båda måste du inkludera både stora och små bokstäver i mönstret.

re.IGNORECASEOm detta anges kommer det att matcha utan att ta hänsyn till stor- och småskalighet. Motsvarar flaggan i i vanliga reguljära uttryck.

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'>

Du kan använda mindre än eller lika med.

  • Inline-flagga(?i)
  • Förkortningre.I

Matcha början och slutet på varje rad:re.MULTILINE

^Meta-tecknen i det här reguljära uttrycket matchar början av strängen.

Som standard matchas endast början av hela strängen, men följande matchning kommer att matcha början av varje rad också. Motsvarar flaggan m i vanliga reguljära uttryck.
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']

$Matchar slutet av strängen. Som standard matchas endast slutet av hela strängen.
re.MULTILINEOm du anger detta kommer den också att matcha slutet av varje rad.

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

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

Du kan använda mindre än eller lika med.

  • Inline-flagga(?m)
  • Förkortningre.M

Ange flera flaggor

|Om du vill aktivera flera flaggor samtidigt använder du detta. När det gäller inline-flaggor måste varje tecken följas av en bokstav enligt nedan.
(?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']

Giriga och icke-giriga matcher

Detta är ett generellt problem med reguljära uttryck, inte bara ett problem med Python, men jag skriver om det eftersom det brukar ställa till det för mig.

Som standard är följande en greedy match, som matchar den längsta möjliga strängen.

  • *
  • +
  • ?
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

? efter den kommer att resultera i en minimal matchning som inte är girig och som matchar den kortast möjliga strängen.

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

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

Observera att standardmatchningen kan matcha oväntade strängar.