Hoe gebruik je de Python reguliere expressie module re (match, search, sub, etc.)

Bedrijf

Om reguliere uitdrukkingen in Python te verwerken, gebruiken we de re module uit de standaard bibliotheek. Hiermee kun je strings extraheren, vervangen en splitsen met behulp van reguliere expressiepatronen.

In dit gedeelte zullen we eerst de functies en methoden van de module re uitleggen.

  • Samenstellen van reguliere expressiepatronen:compile()
  • luciferobject
  • Controleer of het begin van de string overeenkomt, extract:match()
  • Zoek naar overeenkomsten die niet beperkt zijn tot het begin:search()
  • Controleer of de hele string overeenkomt:fullmatch()
  • Haal een lijst van alle passende onderdelen:findall()
  • Krijg alle overeenkomende delen als een iterator:finditer()
  • Vervang het passende onderdeel:sub(),subn()
  • Teksten splitsen met reguliere expressiepatronen:split()

Daarna zal ik de meta karakters (speciale tekens) en speciale reeksen van reguliere expressies uitleggen die gebruikt kunnen worden in de re module. In principe is het de standaard reguliere expressie syntaxis, maar wees voorzichtig met het instellen van vlaggen (vooral re.ASCII).

  • Reguliere expressie metacharacters, speciale sequenties, en waarschuwingen in Python
  • De vlag instellen
    • Beperkt tot ASCII tekens:re.ASCII
    • Niet hoofdlettergevoelig:re.IGNORECASE
    • Zorg dat het begin en het einde van elke regel overeenkomen:re.MULTILINE
    • Specificeer meerdere vlaggen
  • Hebzuchtige en niet hebzuchtige wedstrijden

Compileer het reguliere uitdrukkingspatroon: compile()

Er zijn twee manieren om reguliere expressie verwerking uit te voeren in de re module.

Met functie uitvoeren

De eerste is een functie.re.match(),re.sub()Functies als deze zijn beschikbaar om extractie, vervanging en andere processen uit te voeren met behulp van reguliere expressiepatronen.

De details van de functies worden later beschreven, maar in alle functies is het eerste argument de tekenreeks van het reguliere expressiepatroon, gevolgd door de tekenreeks die verwerkt moet worden, enzovoort. Bijvoorbeeld, in re.sub(), dat substitutie uitvoert, is het tweede argument de substitutiestring, en het derde argument is de te verwerken string.

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

Merk op dat [a-z] in het reguliere uitdrukkingspatroon in dit voorbeeld elk teken van a tot z betekent (d.w.z. alfabet in kleine letters), en + betekent dat het vorige patroon (in dit geval [a-z]) een of meer keren wordt herhaald. De [a-z]+ komt overeen met elke tekenreeks die een of meer alfabetische tekens in kleine letters herhaalt.

. is een meta-teken (een teken met een speciale betekenis) en moet worden ge-escaped met een backslash.

Omdat patroonreeksen van reguliere expressies vaak veel backslashes gebruiken, is het handig om ruwe tekenreeksen te gebruiken zoals in het voorbeeld.

Loopt in een methode van een regelmatig uitdrukkingspatroon object

De tweede manier om reguliere expressies te verwerken in de re module is de reguliere expressie patroon object methode.

Met re.compile() kunt u een reguliere expressiepatroonreeks compileren om een regulier expressiepatroonobject te maken.

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()Hetzelfde proces als deze functies kan bijvoorbeeld worden uitgevoerd als methodes match(),sub() van reguliere expressie-objecten.

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

Alle hieronder beschreven re.xxx() functies worden ook aangeboden als methoden van het reguliere expressie-object.

Als je een proces herhaalt dat hetzelfde patroon gebruikt, is het efficiënter om een reguliere expressie object te genereren met re.compile() en het rond te gebruiken.

In de volgende voorbeeldcode wordt de functie voor het gemak gebruikt zonder te compileren, maar als u hetzelfde patroon herhaaldelijk wilt gebruiken, is het aan te bevelen de functie vooraf te compileren en uit te voeren als een methode van een reguliere expressieobject.

luciferobject

match(), search(), enz. geven een match object terug.

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

De gematchte string en positie worden verkregen met behulp van de volgende methoden van het match object.

  • Zoek de locatie van de wedstrijd:start(),end(),span()
  • Verkrijg de overeenkomende string:group()
  • Verkrijg de string voor elke groep:groups()
print(m.start())
# 0

print(m.end())
# 11

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

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

Als je een deel van een reguliere expressiepatroon insluit in een tekenreeks met haakjes(), wordt het deel verwerkt als een groep. In dit geval kan de string van het deel dat overeenkomt met elke groep in groups() worden verkregen als een 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')

Controleer of het begin van een string overeenkomt, extract: match()

match() geeft een match object terug als het begin van de string overeenkomt met het patroon.

Zoals hierboven vermeld, kan het match object gebruikt worden om de gematchte substring te extraheren, of eenvoudig om te controleren of er een match was.

match() zal alleen het begin controleren. Als er geen overeenkomende string aan het begin is, geeft het None terug.

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

Controleer op overeenkomsten die niet beperkt zijn tot het begin, extract: search()

Net als match(), retourneert het een match object als het overeenkomt.

Als er meerdere overeenkomende delen zijn, wordt alleen het eerste overeenkomende deel geretourneerd.

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

Als je alle overeenkomende delen wilt krijgen, gebruik dan findall() of finditer() zoals hieronder beschreven.

Controleer of de hele string overeenkomt: fullmatch()

Om te controleren of de hele string overeenkomt met het reguliere expressiepatroon, gebruik je fullmatch(). Dit is bijvoorbeeld handig om te controleren of een string geldig is als emailadres of niet.

Indien de volledige string overeenkomt, wordt een match object geretourneerd.

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

Als er niet-overeenkomende delen zijn (slechts gedeeltelijke overeenkomsten of helemaal geen overeenkomsten), wordt Geen teruggezonden.

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

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

De fullmatch() is toegevoegd in Python 3.4. Als je hetzelfde wilt doen in eerdere versies, gebruik dan match() en een overeenkomend meta karakter $ aan het eind. Als de hele string van begin tot eind niet overeenkomt, geeft het None terug.

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

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

Verkrijg een lijst van alle overeenkomende onderdelen: findall()

findall() geeft een lijst van alle overeenkomende substrings. Merk op dat de elementen van de lijst geen overeenkomst-objecten zijn, maar tekenreeksen.

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

Het aantal gematchte delen kan worden gecontroleerd met de ingebouwde functie len(), die het aantal elementen in de lijst teruggeeft.

print(len(result))
# 3

Groeperen met haakjes() in een reguliere expressiepatroon geeft een lijst van tupels waarvan de elementen de strings van elke groep zijn. Dit is equivalent aan groups() in het match object.

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

De groep haakjes () kan genest worden, dus als je ook de hele overeenkomst wilt krijgen, zet je gewoon de hele overeenkomst tussen haakjes ().

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

Als er geen overeenkomst wordt gevonden, wordt een lege tupel geretourneerd.

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

Haal alle overeenkomende delen op als een iterator: finditer()

finditer() geeft alle overeenkomende delen terug als een iterator. De elementen zijn geen strings zoals findall(), maar match objecten, zodat je de positie (index) van de gematchte delen kunt krijgen.

De iterator zelf kan niet worden afgedrukt met print() om de inhoud te krijgen. Als je de ingebouwde functie next() of het for statement gebruikt, kun je de inhoud één voor één krijgen.

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

Het kan ook geconverteerd worden naar een lijst met 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)

Als je de positie van alle overeenkomende delen wilt krijgen, is de list comprehension notatie handiger dan list().

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

De iterator haalt de elementen er in volgorde uit. Merk op dat als je meer elementen probeert te extraheren nadat je het einde hebt bereikt, je niets overhoudt.

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

Vervang de overeenkomende delen: sub(), subn()

Met sub() kun je het gematchte deel vervangen door een andere string. De vervangen string wordt geretourneerd.

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

Bij groeperen met haakjes(), kan de gematchte string gebruikt worden in de vervangen string.

Standaard wordt het volgende ondersteund: Merk op dat voor normale strings die geen ruwe strings zijn, een backslash moet worden vermeld vóór de backslash om de backslash te escapen.

\1De eerste haakjes
\2De tweede haakjes
\3De derde haakjes
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>
Als u de groep een naam geeft door deze aan het begin van de haakjes van het reguliere uitdrukkingspatroon te schrijven, kunt u de groep opgeven met de naam in plaats van het getal, zoals hieronder getoond.
\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

Het argument count specificeert het maximum aantal vervangingen. Alleen het aantal aan de linkerkant wordt vervangen.

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() geeft een tupel terug van de gesubstitueerde string (dezelfde als de return waarde van sub()) en het aantal gesubstitueerde delen (het aantal dat overeenkomt met het patroon).

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

De methode om argumenten op te geven is dezelfde als sub(). U kunt het deel gegroepeerd tussen haakjes gebruiken, of het aantal argumenten opgeven.

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)

Tekenreeksen splitsen met reguliere expressiepatronen: split()

split() splitst de string op het deel dat overeenkomt met het patroon, en geeft het terug als een lijst.

Merk op dat de eerste en laatste overeenkomst lege strings zullen bevatten aan het begin en einde van de resulterende lijst.

s = '111aaa222bbb333'

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

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

Het maxsplit argument specificeert het maximum aantal splitsingen (stukken). Alleen de telling van de linkerkant zal worden gesplitst.

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

Reguliere expressie metacharacters, speciale sequenties, en waarschuwingen in Python

De belangrijkste reguliere expressie meta-tekens (speciale tekens) en speciale sequenties die kunnen worden gebruikt in de Python 3 re module zijn als volgt

metakarakterinhoud
.Elk ander teken dan een nieuwe regel (inclusief een nieuwe regel met de vlag DOTALL)
^Het begin van de string (komt ook overeen met het begin van elke regel met de MULTILINE vlag)
$Het einde van de string (komt ook overeen met het einde van elke regel met de vlag MULTILINE)
*Herhaal het vorige patroon meer dan 0 keer
+Herhaal het vorige patroon ten minste één keer.
?Herhaal het vorige patroon 0 of 1 keer
{m}Herhaal het vorige patroon m keer
{m, n}Het laatste patroon.m~nherhalen
[]Een reeks tekens[]Komt overeen met een van deze tekens
|OFA|BKomt overeen met patroon A of B
speciale opeenvolginginhoud
\dUnicode decimale getallen (beperkt tot ASCII-nummers door ASCII-vlag)
\D\dDat betekent het tegenovergestelde van dit.
\sUnicode witruimtekens (beperkt tot ASCII witruimtekens door ASCII vlag)
\S\sDat betekent het tegenovergestelde van dit.
\wUnicode-woordkarakters en underscores (beperkt tot ASCII-alfanumerieke karakters en underscores door ASCII-flag)
\W\wDat betekent het tegenovergestelde van dit.

Ze staan niet allemaal in deze tabel. Zie de officiële documentatie voor een volledige lijst.

Merk ook op dat sommige betekenissen anders zijn in Python 2.

De vlag instellen

Zoals in de bovenstaande tabel is aangegeven, veranderen sommige metatekens en speciale reeksen van modus afhankelijk van de vlag.

Alleen de hoofdvlaggen worden hier behandeld. Zie de officiële documentatie voor de rest.

Beperkt tot ASCII-tekens: re.ASCII

\wDit zal ook standaard overeenkomen met dubbel-byte kanji, alfanumerieke tekens, enz. voor Python 3 strings. Het is niet gelijkwaardig aan het volgende omdat het geen standaard reguliere expressie is.[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

Indien u re.ASCII specificeert voor de argumentvlaggen in elke functie, of de volgende inline vlag toevoegt aan het begin van de reguliere expressiepatroonreeks, zal het alleen overeenkomen met ASCII-tekens (het zal niet overeenkomen met double-byte Japanse, alfanumerieke tekens, etc.).
(?a)
In dit geval zijn de volgende twee gelijkwaardig.
\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

Hetzelfde geldt voor compileren met re.compile(). Gebruik het argument flags of 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 is ook beschikbaar als de korte vorm re. A. U kunt beide gebruiken.

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

\W, het tegenovergestelde van \W, wordt ook beïnvloed door re.ASCII en inline flags.

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

Net als bij \w komen de volgende twee standaard overeen met zowel single-byte als double-byte tekens, maar ze worden beperkt tot single-byte tekens als re.ASCII of inline vlaggen zijn opgegeven.

  • Maak de nummers\d
  • Zoekt een spatie\s
  • Zoekt niet-nummers\D
  • Zoekt elke niet-spatie.\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

Niet hoofdlettergevoelig:re.IGNORECASE

Standaard is het hoofdlettergevoelig. Om met beide overeen te komen, moet je zowel hoofdletters als kleine letters in het patroon opnemen.

re.IGNORECASEAls dit is opgegeven, zal het hoofdletterongevoelig overeenkomen. Equivalent aan de i vlag in standaard reguliere expressies.

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

U kunt minder dan of gelijk aan gebruiken.

  • inline vlag(?i)
  • afkortingre.I

Zorg dat het begin en het einde van elke regel overeenkomen:re.MULTILINE

^De metatekens in deze reguliere uitdrukking komen overeen met het begin van de string.

Standaard wordt alleen het begin van de hele tekenreeks gematched, maar het volgende zal ook overeenkomen met het begin van elke regel. Equivalent aan de m vlag in standaard reguliere expressies.
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']

$Zoekt het einde van de tekenreeks. Standaard wordt alleen het einde van de hele tekenreeks gematched.
re.MULTILINEAls je dit specificeert, zal het ook overeenkomen met het einde van elke regel.

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

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

U kunt minder dan of gelijk aan gebruiken.

  • inline vlag(?m)
  • afkortingre.M

Specificeer meerdere vlaggen

|Als u meerdere vlaggen tegelijk wilt inschakelen, gebruikt u dit. In het geval van inline flags, moet elk karakter gevolgd worden door een letter, zoals hieronder getoond wordt.
(?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']

Hebzuchtige en niet hebzuchtige wedstrijden

Dit is een algemeen probleem met reguliere expressies, niet alleen een probleem met Python, maar ik zal er toch over schrijven omdat het me vaak in de problemen brengt.

Standaard is het volgende een hebzuchtige overeenkomst, die overeenkomt met de langst mogelijke tekenreeks.

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

De ? erachter zal resulteren in een niet-vette, minimale overeenkomst, die overeenkomt met de kortst mogelijke string.

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

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

Merk op dat de standaard hebzuchtige overeenkomst kan overeenkomen met onverwachte strings.

Copied title and URL