Introductie tot Regular Expressions
1 bericht
• Pagina 1 van 1
Introductie tot Regular Expressions
Inleiding
In deze tutorial probeer ik je zo simpel mogelijk tot Regular Expressions te introduceren. Ik noem ze vanaf hier regexes, dat is de gebruikelijke afkorting. Ik ben soms erg gedetaileerd en maak kleine, simpele stapjes. Maar ik vind dat je pas met regexes kan werken als je het echt helemaal door hebt, er boven staat, je kunt niet uit de losse pols even een regexje uitproberen. Geloof me, dat dacht ik ook, ik heb het geprobeerd, ik lees ook nooit de eerste hoofdstukken van mijn programmeerboeken... laat staan online tutorials.
De meeste regex-tutorials zijn moeilijk voor beginners omdat ze (naast dat ze bijna nooit nederlands zijn) altijd beginnen met de regex. Ik wil nu eens beginnen met het probleem dat een regex oplost, om duidelijk te maken waarom er regexes zijn.
De basis
Wat je als programmeur vaak wil is kijken of een bepaalde string voorkomt in een al dan niet grote lap tekst. Bijvoorbeeld om die string te verwijderen, vervangen met iets anders, te gebruiken om van de lap tekst een array te maken (splitten) of gewoon omdat je een zoek-functie programmeert. De eerste (programmeer-)optie die je hebt is== , hiermee kijk je of de lap tekst exact gelijk is aan de string. Dit is natuurlijk erg beperkt, als je meer wil dan biedt elke programmeertaal je wel een mogelijkheid om te kijken of de string voorkomt, en zo ja je de positie te geven (indexOf() in Javascript, strpos() in PHP). Echter ben je dan nog steeds beperkt in je mogelijkheden, want je moet de string nog steeds exact opgeven.
Wat je graag zou willen is de computer uitleggen hoe de string er ongeveer uit moet zien, maar niet precies. In het simpelste voorbeeld van een e-mail adres bijvoorbeeld, wil je uit kunnen leggen dat er eerst een aantal letters komen, dan een apestaartje, dan weer een aantal letters, dan een punt, en dan 2 tot 6 letters. Dus:
PHP
Natuurlijk kan een e-mail adres ook cijfers bevatten, maar voor dit voorbeeld houden we het even simpel.
Dit patroon, deze voorwaarden waaraan een string moet voldoen om een e-mail adres te zijn, kun je aan een computer uitleggen met een regex. Je geeft dan geen exacte voorbeeld (jan@klaas.piet), maar een beschrijving van de tekst.
Een groot deel van de verwarring die ontstaat bij de eerste kennismaking met deze regexes komt omdat regexes een geheel eigen syntax hebben. Zoals je van de HTML-syntax over gaat naar de PHP-syntax (met<?PHP ) of de Javascript-syntax (met<script type="text/javascript"> ), zo ga je ook van de PHP-syntax over naar de regex-syntax. Tekens die je in PHP of Javascript kent zoals []{}|$&-+*. enzovoorts, krijgen allemaal een andere betekenis. Denk dus niet dat je met je programmeertaal-kennis ook snapt wat de regex doet. Daar komt bij dat in een regex veel tekens ook weer verschillende betekenissen kunnen hebben in verschillende situaties, wat het nog moeilijker maakt om iemand anders z'n regex te begrijpen.
Omdat regexes zo'n andere syntax hebben, en ook eigenlijk een hele taal opzich zijn, worden ze ook anders geparsed, vaak zelfs door een aparte engine. Je moet de regex dus echt los van de programmeertaal gaan zien, een vak apart, geen leuke feature van de taal waar je in werkt.
Dit is het heel kort de basis die je nodig hebt om echt te snappen wat regexes doen en waar je ze voor gebruikt. De rest is eigenlijk gewoon syntax stampen, je moet gewoon weten welke tekens wat doen.
De eerste regex
De simpelste regex is eigenlijk een gewone string, als je de regex-engine vraagt om te zoeken naarhoi in de tekst'Joep zegt "hoi allemaal"' dan zegt de regex-engine 'ja, gevonden'. Verder geen gezeur, geen moeilijke syntax, niks. Maar de reden dat we regexes gingen gebruiken was nu juist omdat we meer willen dan het zoeken naar één bepaalde, exact vastgestelde string. Daarom nu, terug naar het voorbeeld.
PHP
Dit is uiteraard geen regex, dit is een voor mensen leesbaar patroon, we willen een voor computers leesbaar patroon.
We beginnen met te zeggen dat we letters willen zien, a t/m z (hoofd en kleine letters vergeten we even, dat zorgt nu alleen maar voor verwarring). Met[ ] geven we aan dat we een character-class willen opgeven, dat betekent dat één teken van het te vinden e-mailadres moet voorkomen tussen de blokhaken. Als je dus de string 'Joep zegt "hoi allemaal"' doorzoekt op [pqr] , dan vind de regex-engine de p van Joep, want die komt voor tussen de blokhaken. Eigenlijk zegt [pqr] dus "p of q of r". Als we dus een willekeurige letter uit het alfabet ("a of b of c of...") willen vinden, dan voldoet de volgende character-class.
PHP
Maar omdat dit natuurlijk wel eens vaak voor zal komen, kan dit korter als[a-z] , waar - dan zoiets als tot en met betekent.
PHP
We willen dus een letter, sterker, we willen er 1 tot 20, dit geven we aan door achter de class op te geven hoeveel er voor mogen komen.
PHP
Ziet er logisch uit dunkt me. Let wel, zoals al eerder gezegd, de{ en } betekenen hier wel dat we de minimale en maximale aantallen voorkomens van de class instellen, maar deze tekens kunnen soms ook hele andere dingen betekenen.
Na de 1 tot 20 letters moet er een apestaartje komen, deze kunnen we, zoals met de eerste regex 'hoi', gewoon zonder moeilijke syntax in de regex plaatsen.
PHP
Dan weer 1 tot 20 letters.
PHP
En dan een punt. De punt heeft in de regex echter al een betekenis en we willen niet de betekenis van de punt, maar de punt zelf. We moeten hem dan escapen, zoals je een aanhalingsteken escaped binnen de string'Joep komt \'s avonds' .
PHP
En vervolgens 2 tot 6 letters.
PHP
En daar is onze eerste echte regex. Hiep hoi! Ik moet je echter toch nog teleur stellen, ook al snap je het tot nu toe perfect, je kunt nog niet met regex bezig.
Regexes, uitgebreid en ingewikkeld
Even een overzichtje:
Voor het eerst punt (cijfers, underscores, streepjes, meerdere punten), moeten we de character-class gewoonweg uitbreiden met de toegestane tekens. Dus zoiets:
PHP
"Hey, zou je die . niet escapen?" Nee, omdat de punt nu in de character-class staat, hoef je hem niet te escapen. "Dus, in een character-class hoef je niks te escapen?" Fout, stel dat je de ] zelf ook in de character-class wil hebben, dan moet je die wel escapen. Anders ziet de engine dit als het einde van de character-class. Je ziet ook dat ik de - niet escape, terwijl ik hem daarvoor gebruik om a t/m z en 0 t/m 9 reeksen te beschrijven. Dit komt omdat de - daar niet in verband met een reeks staat, en de engine daarom ook zonder escapen snapt dat je gewoon het tekentje - bedoelt.
Dan het punt van het "bevatten" van een e-mail adres. Als je wil dat de tekst die je doorzoekt ook in zijn geheel aan het patroon voldoet, moet je dit expliciet aangeven. Je kunt in een regex met de^ aangeven dat daar het begin van de tekst moet zijn. Als je dus de regex ^Joe loslaat op de string 'Joep zegt: "Joehoe!"' , dan vind hij wel de Joe van Joep, maar niet die van Joehoe, omdat die niet aan het begin staat. Zo kun je met een $ aan het einde, aangeven dat na het patroon het einde van de tekst moet komen. Dus als je oep$ loslaat op 'Joep ruikt poep' , dan vind de engine alleen oep van poep, en niet van Joep.
Als je van beide tekens gebruik maakt, mag er dus niks voor het patroon staan, en niks achter. Onze regex wordt dan zoiets:
PHP
Dan het hoofdlettergevoeligheidspuntje. Regexes kunnen behalve een patroon ook een aantal overige eigenschappen krijgen. Deze worden attributes of mode-modifiers genoemd. Het al dan niet hoofdlettergevoelig zijn van de regex is dus een modus die je in kunt stellen. Hoe je deze instelt is een beetje afhankelijk van de programmeertaal waarin je regexes gebruikt, maar over het algemeen kunnen regexes als volgt gedeclareerd worden:
PHP
Je ziet dat ik nu dus simpelweg 2 slashes heb toegevoegd. Als we bij deze manier van schrijven willen aangeven dat we in hoofdletterongevoeligheids-modus willen werken, plaatsen we eeni (van case-Insensitive achter de laatste /
PHP
Sommige programmeertalen laten het toe om binnen in het patroon te schakelen tussen modi, in deze tutorial laat ik dat buiten beschouwing. We hebben nu onze eerst echt bruikbare en doordachte regex. De kans is best aanwezig dat je deze regex, of een die hier op lijkt, tegen komt als je de javascript achter een formulier-validatie bekijkt.
Samenvatting
Even een korte samenvatting van deze introductiecursus Regexes:
In deze tutorial probeer ik je zo simpel mogelijk tot Regular Expressions te introduceren. Ik noem ze vanaf hier regexes, dat is de gebruikelijke afkorting. Ik ben soms erg gedetaileerd en maak kleine, simpele stapjes. Maar ik vind dat je pas met regexes kan werken als je het echt helemaal door hebt, er boven staat, je kunt niet uit de losse pols even een regexje uitproberen. Geloof me, dat dacht ik ook, ik heb het geprobeerd, ik lees ook nooit de eerste hoofdstukken van mijn programmeerboeken... laat staan online tutorials.
De meeste regex-tutorials zijn moeilijk voor beginners omdat ze (naast dat ze bijna nooit nederlands zijn) altijd beginnen met de regex. Ik wil nu eens beginnen met het probleem dat een regex oplost, om duidelijk te maken waarom er regexes zijn.
De basis
Wat je als programmeur vaak wil is kijken of een bepaalde string voorkomt in een al dan niet grote lap tekst. Bijvoorbeeld om die string te verwijderen, vervangen met iets anders, te gebruiken om van de lap tekst een array te maken (splitten) of gewoon omdat je een zoek-functie programmeert. De eerste (programmeer-)optie die je hebt is
Wat je graag zou willen is de computer uitleggen hoe de string er ongeveer uit moet zien, maar niet precies. In het simpelste voorbeeld van een e-mail adres bijvoorbeeld, wil je uit kunnen leggen dat er eerst een aantal letters komen, dan een apestaartje, dan weer een aantal letters, dan een punt, en dan 2 tot 6 letters. Dus:
PHP
- Code: Alles selecteren
(1 tot 20 letters)@(1 tot 20 letters).(2 tot 6 letters)
Natuurlijk kan een e-mail adres ook cijfers bevatten, maar voor dit voorbeeld houden we het even simpel.
Dit patroon, deze voorwaarden waaraan een string moet voldoen om een e-mail adres te zijn, kun je aan een computer uitleggen met een regex. Je geeft dan geen exacte voorbeeld (jan@klaas.piet), maar een beschrijving van de tekst.
Een groot deel van de verwarring die ontstaat bij de eerste kennismaking met deze regexes komt omdat regexes een geheel eigen syntax hebben. Zoals je van de HTML-syntax over gaat naar de PHP-syntax (met
Omdat regexes zo'n andere syntax hebben, en ook eigenlijk een hele taal opzich zijn, worden ze ook anders geparsed, vaak zelfs door een aparte engine. Je moet de regex dus echt los van de programmeertaal gaan zien, een vak apart, geen leuke feature van de taal waar je in werkt.
Dit is het heel kort de basis die je nodig hebt om echt te snappen wat regexes doen en waar je ze voor gebruikt. De rest is eigenlijk gewoon syntax stampen, je moet gewoon weten welke tekens wat doen.
De eerste regex
De simpelste regex is eigenlijk een gewone string, als je de regex-engine vraagt om te zoeken naar
PHP
- Code: Alles selecteren
(1 tot 20 letters)@(1 tot 20 letters).(2 tot 6 letters)
Dit is uiteraard geen regex, dit is een voor mensen leesbaar patroon, we willen een voor computers leesbaar patroon.
We beginnen met te zeggen dat we letters willen zien, a t/m z (hoofd en kleine letters vergeten we even, dat zorgt nu alleen maar voor verwarring). Met
PHP
- Code: Alles selecteren
[abcdefghijklmnopqrstuvwxyz]
Maar omdat dit natuurlijk wel eens vaak voor zal komen, kan dit korter als
PHP
- Code: Alles selecteren
[a-z]
We willen dus een letter, sterker, we willen er 1 tot 20, dit geven we aan door achter de class op te geven hoeveel er voor mogen komen.
PHP
- Code: Alles selecteren
[a-z]{1,20}
Ziet er logisch uit dunkt me. Let wel, zoals al eerder gezegd, de
Na de 1 tot 20 letters moet er een apestaartje komen, deze kunnen we, zoals met de eerste regex 'hoi', gewoon zonder moeilijke syntax in de regex plaatsen.
PHP
- Code: Alles selecteren
[a-z]{1,20}@
Dan weer 1 tot 20 letters.
PHP
- Code: Alles selecteren
[a-z]{1,20}@[a-z]{1,20}
En dan een punt. De punt heeft in de regex echter al een betekenis en we willen niet de betekenis van de punt, maar de punt zelf. We moeten hem dan escapen, zoals je een aanhalingsteken escaped binnen de string
PHP
- Code: Alles selecteren
[a-z]{1,20}@[a-z]{1,20}.
En vervolgens 2 tot 6 letters.
PHP
- Code: Alles selecteren
[a-z]{1,20}@[a-z]{1,20}.[a-z]{2,6}
En daar is onze eerste echte regex. Hiep hoi! Ik moet je echter toch nog teleur stellen, ook al snap je het tot nu toe perfect, je kunt nog niet met regex bezig.
Regexes, uitgebreid en ingewikkeld
Even een overzichtje:
- Het e-mail adres mag nu geen cijfers, underscores, streepjes of meer dan 1 punt bevatten.
- De tekst die we gaan doorzoeken moet het e-mail adres bevatten, maar hoeft het nog niet te zijn. (als iemand nu invult "Mijn email is jan@klaas.piet!!!!!!!!!!!", dan wordt dit goed gerekend)
- De regex gaat nu nog niet in op hoofdletters.
Voor het eerst punt (cijfers, underscores, streepjes, meerdere punten), moeten we de character-class gewoonweg uitbreiden met de toegestane tekens. Dus zoiets:
PHP
- Code: Alles selecteren
[a-z0-9._-]{1,20}
"Hey, zou je die . niet escapen?" Nee, omdat de punt nu in de character-class staat, hoef je hem niet te escapen. "Dus, in een character-class hoef je niks te escapen?" Fout, stel dat je de ] zelf ook in de character-class wil hebben, dan moet je die wel escapen. Anders ziet de engine dit als het einde van de character-class. Je ziet ook dat ik de - niet escape, terwijl ik hem daarvoor gebruik om a t/m z en 0 t/m 9 reeksen te beschrijven. Dit komt omdat de - daar niet in verband met een reeks staat, en de engine daarom ook zonder escapen snapt dat je gewoon het tekentje - bedoelt.
Dan het punt van het "bevatten" van een e-mail adres. Als je wil dat de tekst die je doorzoekt ook in zijn geheel aan het patroon voldoet, moet je dit expliciet aangeven. Je kunt in een regex met de
Als je van beide tekens gebruik maakt, mag er dus niks voor het patroon staan, en niks achter. Onze regex wordt dan zoiets:
PHP
- Code: Alles selecteren
^[a-z0-9._-]{1,20}@[a-z0-9._-]{1,20}.[a-z]{2,6}$
Dan het hoofdlettergevoeligheidspuntje. Regexes kunnen behalve een patroon ook een aantal overige eigenschappen krijgen. Deze worden attributes of mode-modifiers genoemd. Het al dan niet hoofdlettergevoelig zijn van de regex is dus een modus die je in kunt stellen. Hoe je deze instelt is een beetje afhankelijk van de programmeertaal waarin je regexes gebruikt, maar over het algemeen kunnen regexes als volgt gedeclareerd worden:
PHP
- Code: Alles selecteren
myvar=/^[a-z0-9._-]{1,20}@[a-z0-9._-]{1,20}.[a-z]{2,6}$/
Je ziet dat ik nu dus simpelweg 2 slashes heb toegevoegd. Als we bij deze manier van schrijven willen aangeven dat we in hoofdletterongevoeligheids-modus willen werken, plaatsen we een
PHP
- Code: Alles selecteren
myvar=/^[a-z0-9._-]{1,20}@[a-z0-9._-]{1,20}.[a-z]{2,6}$/i
Sommige programmeertalen laten het toe om binnen in het patroon te schakelen tussen modi, in deze tutorial laat ik dat buiten beschouwing. We hebben nu onze eerst echt bruikbare en doordachte regex. De kans is best aanwezig dat je deze regex, of een die hier op lijkt, tegen komt als je de javascript achter een formulier-validatie bekijkt.
Samenvatting
Even een korte samenvatting van deze introductiecursus Regexes:
- Met regexes kun je een voor computers begrijpbaar patroon opgeven waaraan een tekst (of een deel daarvan) moet voldoen.
- Regexes zijn een taal opzich, los van de programmeertaal waarin je ze gebruikt.
- Veel tekens in een regex kunnen meerdere betekenissen hebben, afhankelijk van de situatie. Wil je geen van die betekenissen maar gewoon het teken zelf opnemen, dan moet je hem escapen met een \ ervoor.
- Regexes kunnen aantal instellingen hebben, zoals hoofdlettergevoeligheid, die je instelt middels een attribute of mode-modifier.
- Met
[ ] maak je een character-class, wat betekent dat als een teken één van de tekens is tussen de[ en] , dat de tekst dan voldoet aan de voorwaarde van die stap. - Met
a-z geef je alle letters van het alfabet aan in een character-class, met0-9 alle cijfers. - Met
{ } geef je aan hoevaak de vorige voorwaarde minimaal en maximaal achter elkaar mag voorkomen. - Met
^ geef je aan dat daar het begin van de tekst die je doorzoekt moet zitten (deze kan dus ook alleen aan het begin van je regex staan). Met$ geef je het eind van de tekst aan.
- Funkwurm
- Oud Teamlid
- Berichten: 373
- Geregistreerd: 06 Nov 2005 16:56
- Woonplaats: Groningen
1 bericht
• Pagina 1 van 1
Wie is er online?
Gebruikers in dit forum: Geen geregistreerde gebruikers en 1 gast