Regular Expressions
Het gebruik van reguliere expressies (zoals ze in het Nederlands heten), is een manier om data mee te valideren, en modificeren. Simpel gezegd houd dat in dat je met reguliere expressies zowel kunt controleren of data wel correct is, als de data naar wens aanpassen. In dit artikel zal in ingaan op reguliere expressies, inallerlei omstandigheden.
Waarom reguliere expressies gebruiken?
Maar waarom zijn reguliere expressies nu zo nuttig? Reguliere expressies zijn nuttig omdat ze in allerlei situaties oplossingen bieden: Zodra je de techniek kent kun je het veel toepassen. Denk bijvoorbeeld aan url rewriting, validatie en modificatie van data binnen PHP en Javascript ASPX .NET, maar ook andere programmeertalen: Sterker nog, bijna iedere taal bied ondersteuning voor dereguliere expressies. Daarom is het zo belangrijk om reguliere expressies te kennen.
Eén van de toepassingen
Om de werking van reguliere expressies te kunnen uitleggen, leg ik eerst een van de toepassingen van reguliere expressies voor: We beginnen met het valideren van data binnen PHP. Hiervoor kent PHP zelfs meerdere functies: ereg(i) en preg_match. De i in de naam van de ereg functie geeft aan of defunctie wel of niet hoofdletter gevoelig is, zoals wel bij meerdere PHP functies van toepassing is. We gebruiken de preg_match functie, omdat deze sneller is en meer mogelijkheden bied, over deze mogelijkheden later meer. Om de werking duidelijk uit te kunnen leggen, begin ik met een voorbeeldje:
<?php
$match = “Hallo wereld”;
if(preg_match(”/Hallo/i”,$match)) {
print “De opgegeven string matched.”;
} else {
print “De opgegeven string matched.”;
}
?>
We zijn in het script een variabele match: We gaan kijken of deze een expressie matched. Daaronder zien wij een if-lus: Wat doen we als de expressie wel matched, en wat als de expressie niet matched. In dit geval is dat gewoon een (duidelijke) uitvoer. Het belangrijkste deel voor nu is het volgende:
/Hallo/i
Je ziet de slashes: Tussen deze slashes staat de expressie zelf, achter de slashes staan de mogelijke modifiers. Hierover later meer, maar de i die er nu staat, geeft aan dat de expressie niet hoofdlettergevoelig is. De expressie die nu te vinden is, is makkelijk: Wanneer er ‘Hallo’ in de string staat, dan matched de string wel, anders niet. Maar er zijn natuurlijk veel meer mogelijkheden, om deze zo duidelijk mogelijk te maken, enkele expressies op een rijtje:
h.+
Match alles wat een “h” bevat, gevolgd door één of meer andere tekens. Het plusteken betekend namelijk één of meerdere keren herhalen, en de punt betekend alle mogelijke tekens.
Mogelijke matches: hallo, henk, haha en aha, maar niet criminalspoint of ah (er volgen geen letters naar de h).
ha?i
Match alles waarin een “h” voorkomt, gevolgd door geen of precies één “a”, en daarna en “i”.
Mogelijke matches: hai, hi en haiii maar niet hallo of haai.
^h.*
Alles wat met een “h” begint, gevolgd door mogelijke andere tekens. Het dakje (^) betekend namelijk begin van de string, en de asterisk geen of meerdere malen. Mogelijke matches: hallo en henk, maar niet aha of criminalspoint.
^c.*point$
Alles wat begint met een “c”, daarna een reeks willekeurige tekens, en daarna
“point”. Het dollarteken geeft dus het einde van de string aan. Mogelijke matches: criminalspoint, cpoint en crimepoint, maar niet phppoint of crimepoints.
a{2}
Alle strings waarin minimal één keer “aa” in voor komt: Getallen tussen accolades betekend: x aantal keer herhalend. Mogelijke matches: aap, namaak-aap, schaap en schaaap, maar niet anders of
criminalspoint.
(ha){1,3}
Je ziet twee nieuwe dingen: De haken geven aan dat het gehele gedeelte moet worden genomen wat er tussen staat, anders gold het gedeelte tussen de accolades alleen voor de “a”, en niet voor de “ha” samen. Twee getallen gescheiden door een accolade betekend: Voorgaande moet tussen a en b keer voorkomen. Mogelijke matches: haha, hahaha en behandeling, maar niet aap of helemaal.
^[A-Za-z]+$
Matched enkel strings die enkel en alleen uit letters bestaan. De rechte haken, houden in: Tekens in de range van. Mogelijke waarde tussen de rechte haken zijn: 0-9, A-Z, a-z, maar ook tekens als spaties, komma’s en punten kunnen in de range staan. Mogelijk matches: haha en criminalspoint maar niet henk09 of 7103AA.
(criminals|point)
Matched alle strings waarin of “criminals” voorkomt, of “point”. De verticale streep betekend dus OF. Mogelijke matches: criminalswebsite, webpoint of criminalspoint, maar niet crimesite of php.
[^0-9]
Matched alles wat geen getallen bevat. Een dakje binnen rechte haken betekent dus NIET.Mogelijke matches: criminalspoint en website, maar niet henk09 of 7103AA.
Praktische voorbeelden
Nu ik jullie de syntax van de reguliere expressies min of meer heb uitgelegd, zal ik enkele praktische voorbeelden geven om duidelijker te maken hoe de expressies werken.
De loginnaam
We beginnen met een reguliere expressie voor een loginnaam. Deze schrijven we
als volgt:
^[A-Za-z0-9_-]{4,16}$
De loginnaam mag enkel bestaan uit te tekens A-Z, a-z, 0-9, een underscore en een streepje. De loginnaam moet minimaal vier tekens bevatten, en maximaal zestien. Als we de expressie bekijken, voldoet die aan alle eisen!
De postcode
Een iets ingewikkeldere expressie is die voor een postcode, deze zal er als volgt uitzien.
^[0-9]{4}( )?[A-Za-z]{2}$
Deze expressie ziet er al aardig ingewikkeld uit. Maar als we hem ontleden valt het wel mee. De string moet beginnen met vier cijfers. Daarop mag één spatie volgen, maar dat hoeft niet (sommige mensen schrijven postcodes met spatie, andere zonder). Vervolgens dienen er nog twee kleine of grote letters te volgen. Al met al matched deze expressie strings als 9999AA, 9999 AA en 1111bb, allemaal geldige postcodes!
Het e-mail adres
Nu gaan we voor een ingewikkelde expressie: Die van een e-mail adres. We moeten eerst weten waar een e-mail adres precies uit bestaat. Een e-mail adres bestaat in ieder geval uit een apenstaartje, gevolgd door een domeinnaam, en een top-level domeinnaam. Top-level domeinnamen bestaan uit twee tot vier tekens (enkele uitzonderingen daar gelaten), en enkel uit letters. Domeinnamen mogen bestaan uit letters, cijfers en een streepje. E-mail adressen mogen dan ook weer onder subdomeinen vallen, waardoor het domeinnaam als het ware ook punten mag bevatten. Maar de domeinnaam mag niet eindigen of beginnen met een punt. De gebruikersnaam van de e-mail is simpel: Deze mag bestaan uit letters, cijfers, punten, underscores en streepjes.
Dit voegen we allemaal samen, en dan krijgen we dit:
([A-Za-z0-9._-]+)@([A-Za-z0-9-]+)([A-Za-z0-9.-]*[A-Za-z0-9-
]+)*\.([A-Za-z){1,4}
Een flinke reguliere expressie zoals je kunt zien. Voor de duidelijkheid heb ik de
gebruikersnaam, domeinnaam en het top-level domeinnaam even gekleurd.
Data manipuleren
Tot nu toe hebben we het enkel gehad over het valideren van data. Maar reguliere expressies bieden nog meer functionaliteiten: Namelijk het manipuleren van data. Dat wordt bijvoorbeeld gebruikt bij UBB codes, en bij het rewriten van urls. Om de werking van reguliere expressies bij het manipuleren van data duidelijk te maken, gebruiken we het volgende stukje code.
<?php
$data = “Hallo wereld”;
print preg_replace(”/Hallo/i”,”Hoi”,$match);
?>
Zoals je kunt zien, lijkt dit stukje code redelijk veel op het stukje code dat we gebruikten om data te valideren. Om data binnen PHP te manipuleren met reguliere expressies hebben we vele functies tot onze beschikking, dit zijn: ereg(i)_replace, preg_replace, preg_replace_callback en preg_filter.
Dit zijn er redelijk wat. We gaan ons voorlopig enkel bezighouden met de preg_replace functie. Hoe werkt deze functie nu: Eigenlijk is het heel simpel, alles wat voldoet aan de expressie wordt vervangen. Dit wordt aangevuld met de mogelijkheid een deel van de expressie ‘terug te zetten’ in de vervanging. De syntax van preg_replace is als volgt:
preg_replace(string $expression, string $replacement, string $string);
Dit spreekt eigenlijk wel voor zich. De functie geeft het resultaat van de vervanging terug. Zoals ik al eerder zei, kun je ook delen uit de expressie terug zetten in de vervanging, dit werkt als volgt, stel we hebben de expressie van de postcode.
<?php
$postcode = “9999AA”;
print preg_replace(”^([0-9]){4}( )?([A-Za-z]){2}$”,”De postcode
bestaat uit de cijfers \\1, gevolgd door de letters \\3.”,$postcode);
?>
Uitvoer script:
De postcode bestaat uit de cijfers 9999, gevolgd voor de letters AA.
Handig! Zoals je ziet, worden de \\1 en \\3 vervangen voor tekens die zijn gematched. Alle haken in de reguliere expressie stellen een getal voor in de vervanging. In dit geval bevat \\1 dus de cijfers van de postcode, \\2 de mogelijke spatie, en \\3 de letters van de postcode.
Reguliere expressies binnen de mod_rewrite module van
Apache
Nu we weten hoe reguliere expressies werken, kunnen we ze gaan toepassen. Een van de veel gebruikte toepassingen is in de mod_rewrite module van Apache. Het is niet voor niet één van de meest genoemde voordelen van Apache ten opzichte van IIS van Microsoft. Mod_rewrite is een zeer handige module,
die met het oog op SEO steeds belangrijker wordt op het internet.
Opmerking: Om de rewrite module van Apache te gebruiken, moet deze natuurlijk wel aanstaan. Meestal staat deze module standaard aan. We beginnen aan de hand van een voorbeeld. Het .htaccess bestand van het voorbeeld ziet er zo uit:
RewriteEngine On
RewriteRule ^gebruikers/([A-Za-z0-9_-]+).html$ profiel.php?p=$1
Dit kleine scriptje, wat niet veel voorstelt, functioneert al. De bovenste regel zorgt ervoor dat de rewrite module wordt gebruikt. Daarna kunnen we met het echte werk beginnen. Wat doet deze regel nu? De volgende link:
http://www.domein.nl/gebruikers/Stefan.html
Zal worden omgezet naar deze link:
http://www.domein.nl/profiel.php?p=Stefan
Niet zo ingewikkeld dus. Je ziet dat Apache er een iets andere manier van vervangen op nahoud als preg_replace van PHP. Waar preg_replace met \\1 werkt, werkt Apache met $1. Dit is niet z’n erg groot verschil. De rewrite module van Apache kent netzoals PHP ook modifiers, maar daar ga ik in deze tutorial niet meer op in. Later in de tutorial ga ik nog wel in op modifiers die PHP kent..
Andere mogelijkheden van de rewrite module
Er zijn nog meer mogelijkheden dan enkel de RewriteRule opdracht. Hieronder enkele mogelijkheden:
RewriteBase Met deze command kun je alle Rewrites naar een bepaalde map sturen. Mochten alle bestand dus ergens anders staan dan de URL’s doen vermoeden, kun je RewriteBase gebruiken om ze gemakkelijk door te sturen.
Voorbeeld: RewriteBase /html
RewriteCond Met deze optie heb je de mogelijkheid voorwaarden te stellen aan een RewriteRule. RewriteOptions Hiermee kun je enkele instellingen veranderen, zoals het maximale aantal rewrites die een URL kan doorgaan. Dit zou enkel nodig zijn wanneer je meer als 10 rewrites nodig hebt.
Wil je nu meer weten over mod_rewrite, kijk dan even op de website van Apache:
http://httpd.apache.org/docs/1.3/mod/mod_rewrite.html (Engels)