Regular expressions
Dit document beschrijft de werking van de regular expression optie van substitution rules. Hoewel dit document niet bedoeld is als bron voor het leren van regular expressions zal er wel een korte introductie gegeven worden voor de nieuwe gebruiker.
Regular expressions in Clonable
Als u al bekend bent met regular expressions (regex) in andere programma's, kan het zijn dat ze binnen Clonable niet altijd werken zoals u verwacht. Binnen Clonable wordt namelijk gebruik gemaakt van een non-backtracking regex engine om problemen te voorkomen. Hierdoor kan het voorkomen dat ingewikkelde regex niet of anders werken dan in een backtracking engine.
De meeste regex engines zijn gebaseerd op Perl Compatible Regular Expressions (PCRE). Deze engines maken vaak gebruik van backtracking, maar dit is vanwege veiligheidsredenen niet geschikt binnen Clonable. Daarom maakt Clonable gebruik van een non-backtracking engine die een subset van PCRE ondersteunt. Dit betekent dat bijvoorbeeld look-aheads niet werken. Mocht u hierover specifieke vragen hebben dan kunt u hierover contact met ons opnemen.
Introductie regular expressions
Indien u al ervaring heeft met regular expressions (regex) dan hoeft u waarschijnlijk dit hoofdstuk niet te lezen. Indien u nog niet bekend bent met regex dan is dit een kleine introductie om te starten. Aan het einde van dit hoofdstuk zullen verdere bronnen vermeld worden waarin u meer kunt oefenen met regex.
Waarvoor wordt een regex gebruikt?
Een regex wordt gebruikt wanneer u meerdere dingen wilt afvangen die veel op elkaar lijken, maar op sommige plekken afwijken. Denk hierbij bijvoorbeeld aan zinnen die de voorraad vermelden met daarin een getal of zinnen waarin verschillende data staan. U kunt dan heel veel verschillende substitution rules maken voor alle mogelijke combinaties, maar dit is vaak niet te doen of kost veel tijd.
Karaktergroepen
Karaktergroepen worden in regex gebruikt om aan te geven wat voor soort teken er op een bepaalde plek mag staan. Een karakter groep wordt altijd tussen twee vierkante haken ([]
) vermeld. Hierbinnen staan dan de toegestane karaketers. Dit kan ook een bereik zijn aan karaketers, zo betekent [0-9]
alle cijfers van 0 tot 9 en [A-Z]
alle hoofdletters. Karaktergroepen kunnen ook gecombineerd worden; [A-Za-z]
matcht bijvoorbeeld alle grote en kleine letters.
// Matcht zowel hallo als Hallo
[Hh]allo
// Matcht elk getal van 100 tot 499:
[1-4][0-9][0-9]
Quantifiers
Zoals in het voorbeeld hierboven te zien is, moest de [0-9]
herhaald worden. Naast het feit dat dit de regex onoverzichtelijk maakt is het ook niet flexibel (het is bijvoorbeeld niet mogelijk een regex te maken die de getallen 1 tot 99 matcht). Om dit probleem op te lossen kunt u quantifiers gebruiken. Deze geven aan hoe vaak het karakter(groep) ervoor voor moet komen. Deze quantifiers worden altijd vermeld tussen accolades ({}
). Hierbinnen staat door komma's gescheiden wat het minimum en maximum is. Indien het minimum en maximum hetzelfde zijn hoeft u slecht één getal te vermelden. Zo betekent {1,3}
minstens 1 en maximaal 3 en betekenen {4,4}
en {4}
beide exact 4.
Daarnaast zijn er ook nog een aantal speciale quantifiers. ?
betekent 0 of 1, +
betekent 1 of meer en *
betekent 0 of meer.
// Matcht hoi en hooi
ho{1,2}i
// Matcht elk getal beginnend met een 1
1[0-9]*
// Matcht elk getal beginnend met een 1 en dat groter of gelijk is aan 10
1[0-9]+
// Matcht elk getal van 1 tot 19
1[0-9]?
// Matcht elk woord (hoofd- en kleine letters) van 6 letters lang
[A-Za-z]{6}
// Probeer het zelf: maak een regex die alle woorden matcht die beginnen met een hoofdletter. (Antwoord: zie voetnoot 1)
+
en *
Indien u in uw regex gebruik moet maken van de +
of *
quantifiers is het meestal een goed idee om +?
of *?
te gebruiken. Door het vraagtaken zal de regex zo min mogelijk matchen en voorkomt u dat het niet werkt. Voorbeeld:
// Tussen [] staat aangegeven wat gematcht wordt
// [<img src="/images/photo.jpg"><a href="/home"]>home</a>
<img src=".*"
// [<img src="/images/photo.jpg"]><a href="/home">home</a>
<img src=".*?"
U kunt zich voorstellen dat het bovenste gedrag in een volledige webpagina problemen oplevert omdat er dan een veel te lange match ontstaat.
(Match) groepen
Het kan voorkomen dat u specifieker wilt zijn dan een karaktergroep met een bepaalde lengte. Ook daar heeft regex een oplossing voor in de vorm van (match) groepen. Een matchgroep wordt geplaatst tussen twee haken (()
). Hierbinnen kan dan een reeks tekens en/of karaktergroepen gezet worden die samen één geheel vormen. Achter deze groepen kan dan weer een quantifier gezet worden.
// Matcht nom, nomnom, nomnomnom en nomnomnomnom
(nom){1,4}
// Matcht elk woord met daarin de letters oe
[A-Za-z]*(oe)[A-Za-z]*
// Probeer het zelf: maak een regex die alle woorden matcht met daarin 2 of 4 klinkers achter elkaar. (Antwoord: zie voetnoot 2)
Daarnaast zijn deze match groepen ook nog heel handig bij met overnemen van data in de replacement van een substitution (bijvoorbeeld getallen). U kunt namelijk in de replacement de inhoud van zo'n groep overnemen door gebruik te maken van $[groepnummer]
. Zie onderstaand simpel voorbeeld voor het verplaatsen van het euro teken in een geld bedrag van voor naar achter het getal:
Original Replacement | Options | |
---|---|---|
Gereserveerde tekens gebruiken
Zoals u misschien al heeft opgemerkt, gebruikt regex een aantal tekens voor de syntax die ook in zinnen voor kunnen komen (bijvoorbeeld ?
of .
). U vraagt zich dan misschien af hoe die tekens zelf kunt gebruiken. Dat is gelukkig zeer eenvoudig: indien u een teken wilt gebruiken zonder dat dat zijn functie uitvoert op de regex, dan plaatst u er een backslash (\
) voor.
// Matcht Hallo. maar ook Hallok of Hallo@, etc.
Hallo.
// Matcht alleen Hallo.
Hallo\.
// Matcht internationaal
(inter)nationaal
// Matcht (in)ternationaal
\(inter\)national
Overige tips
- Een
.
is een wildcard voor elk teken. Met.*
matcht u dus alles (elke hoeveelheid van elk teken). \d
is een alias voor[0-9]
[A-z]
is niet hetzelfde als[A-Za-z]
, maar bevat ook een aantal leestekens