Naar hoofdcontent

Regular expressions

Dit document beschijft 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.

Regex engines

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. 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} beiden 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)
Gebruik van + 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.

(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 zon 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
(in)ternationaal

// Matcht (in)ternationaal
\(in\)ternational

Overige tips

  • Een . is een wildcard voor elk teken. Met .* matcht u dus alles.
  • \d is een alias voor [0-9]
  • [A-z] is niet hetzelfde als [A-Za-z], maar bevat ook een aantal leestekens

Verdere referenties

  • RegExr Voor testen regex en uitleg/documentatie
  • RegexOne Voor oefenen/leren van regex

1 [A-Z][a-z]* 2 [A-Z][a-z]*([aeiou]{2}){1,2}[A-Z][a-z]*