Nu de basis van het gebruik van reguliere expressies in Python via de
re
module is uitgelegd, kan ik eindelijk beginnen met de uitleg van
het echte schrijven van reguliere expressies.
De eenvoudigste reguliere expressie is een string van tekens, die een
patroon beschrijft dat bestaat uit precies die string van tekens. Je mag
ook een verzameling tekens beschrijven middels vierkante haken [
en
]
. Bijvoorbeeld, de reguliere expressie [aeiou]
beschrijft een teken
dat een "a"
, "e"
, "i"
, "o"
, of "u"
is. Dit betekent dat als
[aeiou]
onderdeel is van een reguliere expressie, dat op die locatie
in het patroon één van die letters moet staan (en wel precies één, en
niet meerdere). Bijvoorbeeld, om de woorden "ball"
, "bell"
,
"bill"
, "boll"
en "bull"
te zoeken, kun je de reguliere expressie
b[aeiou]ll
gebruiken.
import re
slist = re.findall( r"b[aeiou]ll", "Bill Gates en Uwe Boll \
dronken Red Bull bij het voetballen in Campbell." )
print( slist )
Wijzig de reguliere expressie hierboven zodat niet alleen de woorden “ball” en “bell” gevonden worden, maar ook “Bill”, “Boll”, en “Bull”.
Je kunt tussen de vierkante haken een min-teken gebruiken tussen twee
tekens om aan te geven dat niet alleen die twee tekens bedoeld worden,
maar ook alle tekens die ertussen liggen. Bijvoorbeeld, de reguliere
expressie [a-dqx-z]
is equivalent met [abcdqxyz]
. Om alle letters
van het alfabet aan te geven, zowel als hoofd- of als kleine letter, kun
je [A-Za-z]
gebruiken.
Bovendien kun je een “dakje” (^
) meteen rechts naast de
vierkante-haak-open plaatsen om aan te geven dat je het tegengestelde
bedoeld van wat er tussen de vierkante haken staat. Bijvoorbeeld,
[^0-9]
geeft aan alle tekens behalve cijfers.
In reguliere expressies, net als in strings, wordt het backslash teken
(\
) gebruikt om aan te geven dat het volgende teken een speciale
betekenis heeft. De speciale tekens die je voor strings kunt gebruiken,
gelden ook voor reguliere expressies, maar reguliere expressies hebben
er meer. Er zijn ook een aantal meta-tekens die op een speciale manier
geïntepreteerd worden. De volgende speciale tekens zijn gedefinieerd (er
zijn er meer, maar deze worden het meest gebruikt):
symbool | betekenis |
---|---|
\b |
woord begrenzing (breedte nul) |
\B |
geen woord begrenzing (breedte nul) |
\d |
cijfer [0-9] |
\D |
geen cijfer [^0-9] |
\n |
nieuwe regel (newline) |
\r |
return |
\s |
spatie (inclusief tabulatie) |
\S |
geen spatie |
\t |
tabulatie |
\w |
alfanumeriek teken [A-Za-z0-9_] |
\W |
geen alfanumeriek teken [^A-Za-z0-9_] |
\/ |
voorwaartse slash |
\\ |
backslash |
\" |
dubbel aanhalingsteken |
\' |
enkel aanhalingsteken |
^ |
start van een string (breedte nul) |
$ |
einde van een string (breedte nul) |
. |
ieder teken |
Merk op dat “breedte nul” betekent dat het teken niet een echt teken in
de string aanduidt, maar een positie in de string tussen twee tekens (of
het begin of einde van de string). Bijvoorbeeld, de reguliere expressie
^A
representeert een string die start met de letter "A"
.
Bovendien kun je tekens of substrings tussen haakjes zetten, wat de
tekens “groepeert.” Binnen een groep kun je een keuze tussen meerdere
(groepjes) tekens aanduiden middels een pipe-line (|
). Bijvoorbeeld,
de reguliere expressie (appel|banaan|peer)
is de string "appel"
of
de string "banaan"
of de string "peer"
.
Je moet je ervan bewust zijn dat sommige van deze speciale tekens (zeker die zonder backslash, de haakjes, en de pipe-line) niet werken zoals aangegeven als ze tussen vierkante haken staan. Bijvoorbeeld, de punt tussen vierkante haken is niet “ieder teken,” maar een echte punt.
Waar reguliere expressie pas echt interessant worden is wanneer herhalingen worden gebruikt. Een aantal van de meta-tekens worden gebruikt om aan te geven dat (een deel van) een reguliere expressie meerdere keren herhaald wordt. De volgende herhalings-operatoren worden vaak gebruikt:
symbool | betekenis |
---|---|
* |
nul of meer keer |
+ |
1 of meer keer |
? |
nul of 1 keer |
{p,q} |
minstens p en hoogstens q keer |
{p,} |
minstens p keer |
{p} |
precies p keer |
Je zet zo’n operator achter het deel van de reguliere expressie dat
herhaald moet worden. Bijvoorbeeld, ab*c
betekent de letter "a"
,
gevolgd door nul of meer keer de letter "b"
, gevolgd door de letter
"c"
. Deze expressie representeert de strings "ac"
, "abc"
,
"abbc"
, "abbbc"
, "abbbbc"
, etcetera.
Als je een herhalingsoperator zet achter een groepje (tussen haakjes),
duidt het op de herhaling van het groepje. Bijvoorbeeld, (ab)*c
representeert de strings "c"
, "abc"
, "ababc"
, "abababc"
,
"ababababc"
, etcetera.
Het matchen van herhalingen gebeurt “gulzig” (Engels: “greedy”). Er wordt altijd geprobeerd het patroon zo vroeg mogelijk in de tekst te matchen, en de herhaling zo breed mogelijk uit te strekken. Bekijk de volgende code:
import re
mlist = re.finditer(r"ba+","Schaap zegt 'baaaaah' tot Ali Baba.")
for m in mlist:
print( "{} is found at {}.".format(m.group(), m.start()))
Wijzig de reguliere expressie in de code hierboven zodat het iedere
"b"
die gevolgd wordt door één of meer "a"
s vindt, waarbij de "b"
eventueel een hoofdletter mag zijn. De uitvoer moet zijn "baaaaa"
,
"Ba"
en "ba"
.
Wanneer je de vorige opgave hebt opgelost, wijzig je de reguliere
expressie zodat hij patronen vindt die bestaan uit een "b"
of "B"
gevolgd door één of meer "a"
s, en dat één of meer keer herhaald. De
uitvoer moet zijn "baaaaa"
en "Baba"
. Je moet hier haakjes bij
gebruiken. Test de reguliere expressie ook uit op een aantal andere
teksten.
Hier is er nog een, die één of meer herhalingen van de letter "a"
zoekt:
import re
mlist = re.finditer(r"a+","Schaap zegt 'baaaaah' tot Ali Baba.")
for m in mlist:
print( "{} is found at {}.".format(m.group(), m.start()))
Als je deze code uitvoert, zie je dat het patroon vijf keer gevonden
wordt: drie keer een enkele "a"
, een dubbele "a"
, en een groep van
vijf "a"
s. Je vraagt je misschien af waarom het proces niet ook de
"a"
s vindt binnen de langere patronen, bijvoorbeeld, de twee groepen
van vier "a"
s die te vinden zijn in de groep van vijf "a"
s. De reden
is dat de finditer()
en findall()
methodes, als ze een match
gevonden hebben, verder gaan zoeken meteen na de laatst gevonden match.
Gewoonlijk is dit het gedrag dat je wilt zien.
Wijzig nu de r"a+"
in de code hierboven in r"a*"
, zodat gezocht
wordt naar nul of meer "a"
s. Voordat je de code uitvoert, probeer te
bedenken wat het resultaat zal zijn. Voer dan de code uit en zie of je
gelijk hebt. Indien niet, snap je dan wel waarom de uitkomst zo is als
hij is?
Je zult wel gemerkt hebben dat reguliere expressies snel complex worden. Het is een goed idee om commentaar erbij te schrijven zodat je ook later je eigen code nog begrijpt.
Met alles wat je tot nu toe geleerd hebt, moet je de volgende opgave kunnen maken. Het is verstandig om deze op te lossen voordat je met de rest van het hoofdstuk verder gaat. De opgave bestaat uit een stuk code dat je moet afmaken door een aantal reguliere expressies in te vullen.
Als je de code hieronder uitvoert, zoekt hij naar alle reguliere
expressies in relist
, in alle strings in slist
. Dan wordt voor
iedere string de nummers afgedrukt van de reguliere expressies waarvoor
matches zijn gevonden. Je doel is om de reguliere expressies in relist
in te vullen volgens de beschrijvingen die in het commentaar ernaast
staan. Merk op dat de eerste zeven reguliere expressies de string als
geheel beschrijven, en die moet je dus laten starten met een dakje en
eindigen met een dollar teken, wat aangeeft dat de expressie de string
van het begin (^
) tot het einde ($
) moet representeren.
import re
# List van strings die worden gebruikt voor testen.
slist = [ "aaabbb", "aaaaaa", "abbaba", "aaa", "bEver ottEr",
"tango samba rumba", " hello world ", " Hello World " ]
# Reguliere expressies die moeten worden ingevuld.
relist = [
r"", # 1. Alleen a's gevolgd door alleen b's, inclusief ""
r"", # 2. Alleen a's, inclusief ""
r"", # 3. Alleen a's en b's, willekeurige volgorde, incl. ""
r"", # 4. Precies drie a's
r"", # 5. Noch a's noch b's, maar "" is toegestaan
r"", # 6. Een even aantal a's (en niks anders)
r"", # 7. Precies twee woorden, ongeacht spaties
r"", # 8. Bevat een woord dat op "ba" eindigt
r"" # 9. Bevat een woord dat begint met een hoofdletter
]
for s in slist:
print( s, ':', sep='', end=' ' )
for i in range( len( relist ) ):
m = re.search( relist[i], s )
if m:
print( i+1, end=' ' )
print()
De correcte uitvoer is:
aaabbb: 1 3
aaaaaa: 1 2 3 6
abbaba: 3 8
aaa: 1 2 3 4
bEver ottEr: 7
tango samba rumba: 8
hello world : 5 7
Hello World : 5 7 9
Zorg dat je deze allemaal kunt oplossen voordat je verder gaat!