Reguliere Expressie / Regular Expression / Regexp / Regex / RE
Nu we in de vorige post lekker bezig geweest zijn met de wildcards in Microsoft Word wil ik graag de oorspronkelijke techniek verder bespreken. De wildcards zijn namelijk een afgeleide van de reguliere expressie syntax (ofwel regexp, regex). Reguliere expressie is echter nog krachtiger dan de wildcards in Word. Reguliere expressie kent meer variabelen en biedt daardoor meer mogelijkheden. Reguliere Expressie kan gebruikt worden in besturingssystemen als Linux en Unix en in programmeertalen als PHP en Java.
Reguliere expressie komt voor uit de wiskundige logica en is uitgevonden door Stephen Cole Kleene. Unix introduceerde deze voor het eerst binnen hun besturingssysteem in de jaren ’70.
Binnen de syntax van reguliere expressie staan bepaalde tekens voor een patroon. De patronen van een aantal belangrijke reguliere expressies staan hieronder vermeld. Vervolgens zullen we deze in een paar voorbeelden gebruiken om het e.e.a. te verduidelijken.
. (punt)
Elke willekeurig teken behalve \n
[] (brackets / vierkante haken)
Tussen de brackets staat een lijst moet mogelijke / toegestane tekens. Tussen de brackets kunnen we ook een dakje en een minteken gebruiken:
[-] (minteken tussen brackets)
Als het minteken tussen dakjes gebruikt wordt dan geeft het een reeks aan. U kunt opgeven: [1|2|3|4|5] of u gebruikt [0-5]. De tekens die je hier kunt opgeven zijn ASCI tekens en geen speciale tekens.
[^] (dakje tussen brackets)
Als het dakje achter de eerste openingsbracket staat dan betekend dit dat de opgegeven waarde exact andersom is. Dus maakt van de opgegeven waarde een negatieve waarde. [^a-z] betekend alles behalve de kleine letters a tot z.
^ (dakje)
Dit teken staat voor het begin van een regel/string
$ (Dollar teken)
Dit betekend het einde van een regel/string
| (pipe)
Met de “pipe” wordt een keuze weergegeven. Rood|Wit betekend rood of wit
+ (plus)
De plus achter een teken geeft aan dat het vorige teken ten minste 1x moet voorkomen maar dit mag ook oneindig vaak. 211+ kan dus zijn: 211 of 2111 of 21111 etc. Maar niet 21
? (vraagteken)
Het vraagteken achter een teken geeft aan dat een teken maximaal 1x mag voorkomen. 211? Kan dus zijn 211 en 21 maar niet 2111
* (sterretje)
Een sterretje achter een teken betekend dat dit teken 0x of vaker voor mag komen. 211* kan dus zijn: 21, 211, 2111, 21111 etc.
\n
Starten op een nieuwe regel / lijn
\w
Deze combinatie betekend een woord karakter (letter). \W (hoofdletter W) betekend geen woord karakter.
\s
De \s betekend een whitespace ofwel een spatie. \S (hoofdletter S) betekend geen whitespace of spatie
\b
Woordengrens. Dit teken voldoet aan een woordengrens zoals een spatie of enter
() (haakjes)
Tekens die tussen haakjes staan worden als patroongroep gedefinieerd. (groot|klein)er is hetzelfde als groter|kleiner. Daarnaast zijn patroongroepen te gebruiken als variabelen. Hierover vertel ik je later in dit artikel meer.
{} (brackets
Tussen brackets wordt vermeld hoe vaak een karakter voor mag komen. S{3} betekend SSS en S{1,3} betekend S, SS of SSS. Tussen brackets geven we een bereik dus op met een komma en niet met een minteken (-).
\ (backslash)
De backslash wordt vaak als “escape” karakter gebruikt. Stel voor dat je wilt zoeken op “.nl”. De punt is ook een regexp karakter. Dit kan dus betekenen dat er een willekeurig teken voor de .nl mag staan, bijvoorbeeld anl, xnl en #nl. We willen echter zoeken op .nl. Om de punt als een tekstkarakter te laten gelden (en niet als regexp karakter) zetten we hier een \ voor. Het commando wordt dan \.nl
*let op: opgegeven tekens binnen reguliere expressie zijn hoofdletter gevoelig.
Voorbeelden
Snappen jullie al waarom reguliere expressie in veel gevallen ontzettend handig kan zijn? Nee? Bekijk dan snel deze voorbeelden:
1.
Taak: Zoek alle regels/strings die beginnen met het woord “start”
Expressie:
^start |
Uitleg: Het dakje betekend het begin van een regel. Vervolgens zoeken we naar het woord start.
2.
Taak: Zoek alle regels/strings die eindigen met het woord “stop”
Expressie:
stop$ |
Uitleg: De dollar aan het einde van het woord “stop” betekend dat het de regel moet eindigen met stop. Deze reguliere expressie kan ook het woord “stop” zelf als resultaat geven. Als je echt wilt dat het resultaat langere woorden bevat die eindigen op stop dan moeten we zeggen dat er minimaal 1 willekeurig teken voor het woord stop moet komen. Om dit te doen gebruiken we [a-zA-Z]+. De plus zorgt ervoor dat de letters binnen de brackets minimaal 1 keer of vaker voorkomen. Het gaat hier dan over de letters a tot z en A tot Z. Het hele commando wordt dan [a-zA-Z]+stop$
3.
Taak: Zoek alle woorden die beginnen met “ start”
Expressie:
\sstart[a-z]* |
Uitleg: De \s aan het begin betekend een spatie. [a-z]* betekend dat de letters a-z achter het woord 0x of vaker voor mogen komen. Ook het woord “ start” wordt nu in de resultaten meegenomen. Als je zoekt naar langere woorden dan kun je i.p.v. het * een + gebruiken. Het commando wordt dan: \sstart[a-z]+
4.
Taak: Zoek alle woorden waar een letter s of r in voorkomt
Expressie:
[sr] |
Uitleg: De letters tussen de brackets geven de lijst aan met mogelijkheden. De mogelijk voorkomende letters zijn s en r
5.
Taak: Zoek naar alle woorden waar minimaal 2x de letter “t” in voorkomt
Expressie:
[t{2,10}] |
Uitleg: De t staat opgegeven in de lijst met mogelijkheden. Tussen de brackets geven we aan hoe vaak we willen dat deze letter voorkomt. We willen dus dat de letter t 2x of vaker voorkomt en daarom geven we 2 tot 10x op. Een mooiere oplossing waarbij we geen bereik op hoeven te geven is: [t.*t] waarin we zoeken naar woorden waar de letter t in voorkomt gevolgd door 0 of meer andere tekens (.*) en vervolgens weer de letter t bevatten.
6.
Taak: Zoek alle woorden waarin de letter a niet voorkomt
Expressie:
[^a] |
Uitleg: De letter a staat opgegeven tussen de brackets. Het dakje (^) na het openen van de bracket maakt hier echter een negatieve waarde van. De letter a mag nu dus juist niet voorkomen.
7.
Taak: Zoek alle regels/strings waarin de letter a niet voorkomt
Expressie:
^[^a]$ |
Uitleg: De \s aan het begin betekend een spatie. Het woord begint dus na de spatie echt met “start”. [a-z]* betekend dat de letters a-z achter het woord 0x of vaker voor mogen komen. Ook het woord “start” wordt nu in de resultaten meegenomen. Als je zoekt naar langere woorden dan kun je i.p.v. het * een + gebruiken. Het commando wordt dan: \sstart[a-z]+
RegExp verzamelnamen
Er zijn veel meer mogelijkheden met reguliere expressie mogelijk dan ik in deze post kan behandelen. Goed om te weten is dat er veel meer opties zijn als hierboven aangegeven. Zoals je gelezen hebt kun je tussen brackets een bereik opgeven met het minteken. Dit zijn echter alleen ASCII tekens en kunnen dus geen speciale tekens bevatten. Speciaal hiervoor zijn er verzamelnamen gemaakt. Deze verzamelnamen staan tussen brackets met een dubbele punt. Zorg ervoor dat u de originele brackets en de brackets van de verzamelnamen netjes opent en sluit.
[:alpha:]
Een letter
[:lower:]
Een kleine letter
[:upper:]
Een hoofdletter
[:blank:]
Een spatie of een tab
[:space:]
Een Spatie, tab, form feed, verticale tab, nieuwe regel, enter
[:digit:]
Een decimaal cijfer
[:xdigit:]
Een hexadecimaal cijfer
[:alnum:]
Een letter of cijfer
[:punct:]
Alle leestekens
[:graph:]
Een zichtbaar, printable karakter (geen spatie/tab)
[:print:]
Alles behalve control karakter
[:cntrl:]
Een control karakter
Bovenstaande kan handig zijn als je zoekt naar een woord met een willekeurig aantal letters (ook niet ASCII) gevolgd door b.v. het cijfer 1 tot 10. Je kunt dan de volgende reguliere expressie gebruiken:
[[:alpha:]+1-10]
RegExp groepen
Alles wat tussen haakjes geplaatst wordt is automatisch een groep / variabele geworden. We kunnen ook terug refereren aan deze variabele.
(ik heet) (Jarno Baselier)
Bovenstaande voorbeeld heeft 2 groepen.
1e = ik heet
2e = Jarno Baselier
Om terug te refereren aan de eerste groep gebruik je de reguliere expressie \1 en om terug te refereren aan de 2e groep gebruik je \2.
Met groepen kunnen we hele handige dingen doen. B.v.
1.
Taak: Sla alle IPv4 adressen op in een variabele
Expressie:
\b([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})\b |
Uitleg:
Een IPv4 adres bestaat uit 4 octetten gescheiden door een punt. Deze octetten hebben een cijfer van 0 tot 255. Dit kan dus 192.168.1.1 zijn of 10.133.12.6. Onze regexp beginnen en eindigen we met een \b waarbij we zeggen dat onze expressie 1 woord moet zijn (dus niet 192.168.1.1tje). Vervolgens gebruiken we de haakjes om de expressie op te slaan zodat we deze eventueel kunnen gebruiken door terug te refereren met \1. Het IP adres matchen we met [0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}. Dit is 4 keer (dus voor elk octet) dezelfde truck, namelijk: [0-9]{1,3}. Hiermee zeggen we: zoek naar een cijfer tussen 0 en 9 en er moet minimaal 1x een cijfer voorkomen en er mogen maximaal 3 cijfers voorkomen. Het octet wordt gescheiden door een punt. Omdat de punt ook en regexp karakter is “excapen” we die met de backslash (\.). Er wordt dus gezocht naar 4 octetten gescheiden door een punt en elk octet bevat minimaal 1 cijfer en maximaal 3 cijfers.
2.
Taak: Sla alle IPv4 adressen op in een variabele en vervang alle IP adressen voor hetzelfde IP adres incl. het voorvoegsel “IP Adres: “.
Zoek expressie:
\b([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})\b |
Vervang expressie:
IP Adres: \1 |
Uitleg:
We zoeken naar het IP adres op dezelfde manier als in de opdracht hierboven. Omdat het IP adres is opgeslagen in een variabele kunnen we hieraan terug refereren. We hebben per gevonden match slechts 1 variabele (het hele IP adres) en dus gebruiken we \1 om hieraan terug te refereren.
3.
Taak: In een lijst met namen genoteerd als ‘voornaam achternaam” zorg ervoor dat deze namen als volgt genoteerd worden: “achternaam, voornaam”
Zoek expressie:
^([a-zA-Z])+\s([a-zA-Z])+$ |
Vervang expressie:
\2, \1 |
Uitleg:
We zoeken per regel naar een format: “voornaam” “achternaam”. We doen dit door te zoeken naar een woord bestaande uit de letters a-z en A-Z. Dit woord moet minimaal 1 karakter lang zijn. Het commando [a-zA-Z]+ zorgt hiervoor. Vervolgens scheiden we deze door een \s spatie. Daarna zoeken we weer naar een woord (de achternaam). Zowel de voornaam als achternaam staan tussen haakjes en zijn dus variabelen geworden. Ook beginnen we de regexp met een ^ (begin van de regel) en eindigen we met een $ (einde van de regel). Nu we de voornaam en achternaam in een variabele hebben (voornaam is variabele 1 en achternaam is variabele 2) kunnen we deze terug oproepen in de vervang syntax en plaatsen we er een komma tussen.
Nog een reguliere expressie dan om het niet af te leren 🙂
\b(\w)?(\w)\w?\2\1\b |
Bovenstaande expressie is een expressie voor het zoeken woord van 2 tot 5 letters die ook achteruit hetzelfde gelezen kan worden (palindroom). Je kunt de regexp ook als volgt maken (door de \w te vervangen wordt hij beter leesbaar):
\b([a-z])?([a-z])[a-z]?\2\1\b |
Uitleg door de regexp in 7 stapjes te breken:
1.
\b = wordgrens, dus begin van een nieuw woord
2.
([a-z])? = 1 willekeurige letter van a-z en sla deze op in variabele \1
3.
([a-z]) = 1 willekeurige letter van a-z en sla deze op in variabele \2
4.
[a-z]? = 1 willekeurige letter van a-z
5.
\2 = het woord moet op de op een na laatste letter eindigen met de letter van variabele 2
6.
\1 = het woord moet eindigen op de letter van variabele 1
7.
\b = woordgrens, dus einde van het woord
Het woord “lol” voldoet hieraan.
l (stap 2 = var1)
o (stap 3 = var2 + stap 5)
l (stap 4 + stap 6)
Ook het woord “nepen” voldoet hieraan:
n (stap 2 = var1)
e (stap 3 = var2)
p (stap 4)
e (stap 5)
n (stap 6)
Het woord “nessen” voldoet hier niet aan omdat het woord te lang is. We kunnen maximaal 5 letters lang gaan en minimaal 2 letters. We hebben namelijk minimaal 2 en maximaal 3 vaste letters en 2 optionele match tekens waarop het woord moet eindigen.
Hopelijk kunnen jullie iets met deze korte introductie van reguliere expressies. Als je eenmaal gewent bent aan de opbouw en structuur kun je heel veel, heel erg snel zoeken en modificeren. Veel plezier!!