# Gepost op 18-08-2009 23:27
Bewerkt door vlerknozem op 19-08-2009 00:49
Bewerkt door vlerknozem op 19-08-2009 00:49
Reguliere expressies, beter bekend als RegEx worden regelmatig gebruikt. Met een stukje tekst (pattern) kan je bepaalde delen van een string verwijderen of veranderen. Het handige is dat de pattern syntax over het algemeen bij alle programmeertalen hetzelfde is. Maar de zaak erom heen, hoe je die pattern kan gebruiken, verschilt dan wel weer. Ik ga me nu bezighouden met php.
We gaan eerst simpel beginnen. We maken een string, en controleren of het woord 'noot' in de string voorkomt. In dit geval is het hoofdlettergevoelig.
Nu willen we zorgen dat het niet meer hoofdletter gevoelig is. Dit kan met een i achter de '/'.
In dit geval zal hij 'NOOT' toch vinden. Als we in het vorige voorbeeld 'noot' met hoofdletters hadden geschreven, dan zouden we geen match hebben.
Nu willen we kijken of het woord 'aap' aan het begin van de string staat. Om het voor mij makkelijker te maken, ga ik nu niet meer steeds een if structuur eromheen zetten. Als er een match is zal preg_match de waarde 1 returen. Bij geen match is het dus 0.
Als we willen controleren of aap aan het einde van de string staat:
Als je nu even nadenkt dan zou je nu ook moeten weten hoe je controlleert op een woord alleen in de string staat.
Nu gaan we het wat ingewikkelder maken. Stel wel willen met een pattern checken of het woord een van de woorden 'bal', 'bol', 'bil' is. Dit doen we met karaktersets. Hiervoor worden de tekens '[' en ']' gebruikt.
Bovenstaand voorbeeld wordt het woord 'voetbal' niet gematched. Waarom niet? Omdat 'bal' wel op het einde, maar niet op het begin van de string staat. Hier ga ik verder niet meer op in, omdat ik dit eerder al heb uitgelegd. Maar we kunnen meer met die karaktersets. In het volgende voorbeeld wil ik alles dat begint met een b en eindig op l gaan vervangen door vis.
Maar nu gaan we hetzelfde doen, maar dan met meerdere karakters. Het teken \ gebruiken we om karakters die ook door regex zelf worden gebruikt te escapen. Doen we dit niet dan zal er een fout optreden.
In bovenstaand voorbeeld zie je dat 'b99l' dus niet wordt vervangen. Waarom? Omdat er minimaal 1, en maximaal 1 karakter voorkomt tussen de 'b' en de 'l'. Nu willen we dus we dus ook een resultaat hebben als er geen karakter is, en als er meerdere karakters zijn tussen de b en de l. Hiervoor gebruiken we meta-karakters.
? Matched 0 of 1 karakter uit het karakterset
+ Matched 1 of meer karakter uit het karakterset
. Matched meer dan 1 karakter uit het karakterset
Dit gaan we in praktijk brengen met hetvolgende voorbeeld:
Nu gaan we hetzelfde doen, maar dan even in een gewone string, zonder karaktersets:
Maar nu willen we het aantal karakters zelf aangeven in cijfers. Hiervoor gebruiken we de tekens '{' en '}'.
{0,2} 0 tot 2
{2,} 2 of meer
{1,2} 1 of 2
De pattern /ph{0,2}p/ matched dus pp, php en phhp. Maar niet phhhp, phhhhp, enzovoorts.
De pattern /ph{2,}p/ matched dus phhp, phhhp, enzovoorts. Maar niet php of pp.
Uitgebreid aanpassen van strings
We gaan nu gebruik maken van haakjes '(' en ')'. Ook gaan we gebruik maken van '*'. Stel we willen alles tussen [b] en [/b] tussen <b> en </b> zetten. Dit is handig voor een ubb parser.
Wat betekend '.*?' nu precies? Het sterretje in '.*?' staat voor alle karakters. Dus dit kan een cijfer zijn, of een letter, maar ook andere tekens. De andere tekens heb ik eerder uitgelegd. De \\1 in de vervangende string zegt dat op die plek de gematchte tekst tussen de eerste haakjes moet komen. Ik geef even een voorbeeld met meer haakjes.
Het eerste deel ([^a-z ]+) matched alle letters en spaties, het tweede deel ([^0-9]+) matched alle cijfers.
Het ^ teken
Eerder zag je dat het ^ teken wordt gebruikt voor het matchen van iets aan het begin van de string. Maar zoals je ziet in het vorige voorbeeld, wordt het ook gebruikt om alle karakters die niet gelijk zijn aan iets te matchen. Wil je bijvoorbeeld ALLE tekens BEHALVE letters verwijderen, dan gebruik je:
Het | teken
Deze heb ik nog niet uitgelegd. Het betekend hetzelfde als 'of' (in het engels 'or').
Dit kan dus handig zijn om illegale karakters uit een bestandsnaam te wissen.
Speciale tekens
De backslash kan ook gebruikt worden voor andere zaken dan alleen escapen.
\d matched alle numerieke karakters, hetzelfde als [0-9]
\D matched alle niet nummerieke karakters, hetzelfde als [^0-9]
\s matched alle witruimte, hetzelfde als [ \t\n\r\f\v]
\S matched alle niet witruimte, hetzelfde als [^ \t\n\r\f\v]
\w matched alle alpha numerieke karakters, hetzelfde als [a-zA-Z0-9_]
\W matched alle niet alpha numerieke karakters, hetzelfde als [^a-zA-Z0-9_]
Controlleren of een email adres geldig is
Met bovenstaande uitleg zou je dus een simpele controlle kunnen doen.
Maar natuurlijk is dit nog niet helemaal goed. Er mogen meerdere tekens voorkomen, en het moet niet hoofdletter gevoelig zijn.
Omdat het nu ingewikkelder eruit ziet, zal ik even de delen uit elkaar halen.
[a-z0-9_\-\.]+ matched letters, cijfers en de tekens '_', '-', en '.'.
@ matched het apenstaartje in een email adres
[a-z0-9_\-\.]+ komt nog een keer voor.
\. matched de . in een email
[a-z]{0,3} matched de extensie (nl, com, org, etc).
Matches naar een array plaatsen
Met preg_match kan je de gematchte dingen naar een array plaatsen. Laten we eens kijken.
Zal dus weergeven:
Nu even een vrij lastige
Ik geef nu een voorbeeld van een vrij ingewikkelde regexp. Het is de bedoeling dat $beker="kofie"; vervangen wordt door 'De beker is gevuld met koffie.'. Maar er mogen spaties in komen, de quotes mogen weg gelaten worden, en beker mag ook iets anders zijn, en koffie mag ook iets anders zijn.
Lastig he? Goed ik probeer het iets te verklaren.
$beker matchen
\$ Deze matched dus de '$'.
([a-z]+) matched alleen letters.
Spaties tussen '$beker' en '=', en tussen '=' en '"' matchen
\s{0,} Matched 0 of meer spaties (of andere witruimte karakters).
De quotes
["|\']? matched 0 of 1 dubbele quote, of 0 of 1 enkele quote
De puntkomma
Het ';' matches vanzelfspreken het ';' teken, en sluit de variabele af.
Probeer nu voor de grap $beker eens te vervangen door $kan. Of vervang "koffie" eens door "thee".
Deze lastige nog een keer, maar dan met preg_match
De volgende code zal het zelfde weergeven als het vorige voorbeeld
Maar wat nu als we meerdere variabelen willen gebruiken? Een voorbeeld:
resulteert:
Waar is dan die kan thee gebleven? Hiervoor kunnen we preg_match_all gebruiken.
Resulteert:
Om deze gegevens nu te gebruiken, kunnen we bijvoorbeeld doen:
En je ziet verschijnen:
Een functie gebruiken om iets te doen met de matches
Het kan soms handig zijn om een functie te gebruiken om iets te doen met de matches. Ik gebruik hiervoor weer dezelfde regex. We gaan nu gebruik maken van preg_replace_callback.
Dit heeft hetzelfde resultaat als het vorige voorbeeld. Bij OOP werkt dit iets anders. Op de plek van 'callback_functie' komt dan een array met $this erin, en de functie naam. Een klein voorbeeld:
Tot slot
Dit was erg veel. Ik raad aan om gewoon te experimenteren hiermee. En natuurlijk hoef je het niet in een keer uit je hoofd te weten, je kan altijd even terug kijken naar hoe het ook alweer zat. Wellicht dat ik de tutorial nog iets ga aanpassen om eventuele fouten te corrigeren, of dingen beter uit te leggen.
Succes!
We gaan eerst simpel beginnen. We maken een string, en controleren of het woord 'noot' in de string voorkomt. In dit geval is het hoofdlettergevoelig.
- <?php
- // Onze string
- $string = 'aapnootmies';
- echo "Het woord noot komt voor in de string.";
- } else {
- echo "Het woord noot is niet gevonden.";
- }
- ?>
Nu willen we zorgen dat het niet meer hoofdletter gevoelig is. Dit kan met een i achter de '/'.
- <?php
- // Onze string
- $string = 'aapNOOTmies';
- echo "Het woord noot komt voor in de string.";
- } else {
- echo "Het woord noot is niet gevonden.";
- }
- ?>
In dit geval zal hij 'NOOT' toch vinden. Als we in het vorige voorbeeld 'noot' met hoofdletters hadden geschreven, dan zouden we geen match hebben.
Nu willen we kijken of het woord 'aap' aan het begin van de string staat. Om het voor mij makkelijker te maken, ga ik nu niet meer steeds een if structuur eromheen zetten. Als er een match is zal preg_match de waarde 1 returen. Bij geen match is het dus 0.
- <?php
- // Je zal dus nu zien: 10
- ?>
Als we willen controleren of aap aan het einde van de string staat:
- <?php
- // Je zal dus nu zien: 100
- ?>
Als je nu even nadenkt dan zou je nu ook moeten weten hoe je controlleert op een woord alleen in de string staat.
- <?php
- ?>
Nu gaan we het wat ingewikkelder maken. Stel wel willen met een pattern checken of het woord een van de woorden 'bal', 'bol', 'bil' is. Dit doen we met karaktersets. Hiervoor worden de tekens '[' en ']' gebruikt.
- <?php
- ?>
Bovenstaand voorbeeld wordt het woord 'voetbal' niet gematched. Waarom niet? Omdat 'bal' wel op het einde, maar niet op het begin van de string staat. Hier ga ik verder niet meer op in, omdat ik dit eerder al heb uitgelegd. Maar we kunnen meer met die karaktersets. In het volgende voorbeeld wil ik alles dat begint met een b en eindig op l gaan vervangen door vis.
- <?php
- ?>
Maar nu gaan we hetzelfde doen, maar dan met meerdere karakters. Het teken \ gebruiken we om karakters die ook door regex zelf worden gebruikt te escapen. Doen we dit niet dan zal er een fout optreden.
- <?php
- echo preg_replace('/b[a-z0-9_\-]l/', 'vis', 'b4llon'); // In dit geval wordt b4llon vervangen door vislon
- ?>
In bovenstaand voorbeeld zie je dat 'b99l' dus niet wordt vervangen. Waarom? Omdat er minimaal 1, en maximaal 1 karakter voorkomt tussen de 'b' en de 'l'. Nu willen we dus we dus ook een resultaat hebben als er geen karakter is, en als er meerdere karakters zijn tussen de b en de l. Hiervoor gebruiken we meta-karakters.
? Matched 0 of 1 karakter uit het karakterset
+ Matched 1 of meer karakter uit het karakterset
. Matched meer dan 1 karakter uit het karakterset
Dit gaan we in praktijk brengen met hetvolgende voorbeeld:
- <?php
- // Met een '?'
- // Met een '+'
- // Met een '.'
- ?>
Nu gaan we hetzelfde doen, maar dan even in een gewone string, zonder karaktersets:
- <?php
- ?>
Maar nu willen we het aantal karakters zelf aangeven in cijfers. Hiervoor gebruiken we de tekens '{' en '}'.
{0,2} 0 tot 2
{2,} 2 of meer
{1,2} 1 of 2
De pattern /ph{0,2}p/ matched dus pp, php en phhp. Maar niet phhhp, phhhhp, enzovoorts.
De pattern /ph{2,}p/ matched dus phhp, phhhp, enzovoorts. Maar niet php of pp.
Uitgebreid aanpassen van strings
We gaan nu gebruik maken van haakjes '(' en ')'. Ook gaan we gebruik maken van '*'. Stel we willen alles tussen [b] en [/b] tussen <b> en </b> zetten. Dit is handig voor een ubb parser.
- <?php
- ?>
Wat betekend '.*?' nu precies? Het sterretje in '.*?' staat voor alle karakters. Dus dit kan een cijfer zijn, of een letter, maar ook andere tekens. De andere tekens heb ik eerder uitgelegd. De \\1 in de vervangende string zegt dat op die plek de gematchte tekst tussen de eerste haakjes moet komen. Ik geef even een voorbeeld met meer haakjes.
- <?php
- echo preg_replace('/([^a-z ]+)([^0-9]+)/i', '\\2, en is \\1 jaar geworden.', 'Een 33 jarige aap genaamd mies eet een noot');
- // Een jarige aap genaamd mies eet een noot, en is 33 jaar geworden.
- ?>
Het eerste deel ([^a-z ]+) matched alle letters en spaties, het tweede deel ([^0-9]+) matched alle cijfers.
Het ^ teken
Eerder zag je dat het ^ teken wordt gebruikt voor het matchen van iets aan het begin van de string. Maar zoals je ziet in het vorige voorbeeld, wordt het ook gebruikt om alle karakters die niet gelijk zijn aan iets te matchen. Wil je bijvoorbeeld ALLE tekens BEHALVE letters verwijderen, dan gebruik je:
Het | teken
Deze heb ik nog niet uitgelegd. Het betekend hetzelfde als 'of' (in het engels 'or').
- <?php
- // Een fruitstuk of fruitstuk is lekker
- ?>
- <?php
- ?>
Dit kan dus handig zijn om illegale karakters uit een bestandsnaam te wissen.
Speciale tekens
De backslash kan ook gebruikt worden voor andere zaken dan alleen escapen.
\d matched alle numerieke karakters, hetzelfde als [0-9]
\D matched alle niet nummerieke karakters, hetzelfde als [^0-9]
\s matched alle witruimte, hetzelfde als [ \t\n\r\f\v]
\S matched alle niet witruimte, hetzelfde als [^ \t\n\r\f\v]
\w matched alle alpha numerieke karakters, hetzelfde als [a-zA-Z0-9_]
\W matched alle niet alpha numerieke karakters, hetzelfde als [^a-zA-Z0-9_]
- <?php
- // 'ABCDEFGab cdefg ae* &z()F-+Q?'
- // '12345'
- // 'ABCDEFGabcdefg123a45e*&z()F-+Q?'
- // ' '
- // ' * &()-+?'
- // 'ABCDEFGabcdefg123a45ezFQ'
- ?>
Controlleren of een email adres geldig is
Met bovenstaande uitleg zou je dus een simpele controlle kunnen doen.
- <?php
- echo 'Geldig';
- } else {
- echo 'Ongeldig';
- }
- ?>
Maar natuurlijk is dit nog niet helemaal goed. Er mogen meerdere tekens voorkomen, en het moet niet hoofdletter gevoelig zijn.
- <?php
- ?>
Omdat het nu ingewikkelder eruit ziet, zal ik even de delen uit elkaar halen.
[a-z0-9_\-\.]+ matched letters, cijfers en de tekens '_', '-', en '.'.
@ matched het apenstaartje in een email adres
[a-z0-9_\-\.]+ komt nog een keer voor.
\. matched de . in een email
[a-z]{0,3} matched de extensie (nl, com, org, etc).
Matches naar een array plaatsen
Met preg_match kan je de gematchte dingen naar een array plaatsen. Laten we eens kijken.
- <?php
- ?>
Zal dus weergeven:
- (
- [0] => [b]test[/b]
- [1] => test
- )
Nu even een vrij lastige
Ik geef nu een voorbeeld van een vrij ingewikkelde regexp. Het is de bedoeling dat $beker="kofie"; vervangen wordt door 'De beker is gevuld met koffie.'. Maar er mogen spaties in komen, de quotes mogen weg gelaten worden, en beker mag ook iets anders zijn, en koffie mag ook iets anders zijn.
- <?php
- echo preg_replace('/\$([a-z]+)\s{0,}=\s{0,}["|\']?([a-z]+)["|\']?;/i', 'De \\1 is gevuld met \\2.', '$beker = "koffie";');
- ?>
Lastig he? Goed ik probeer het iets te verklaren.
$beker matchen
\$ Deze matched dus de '$'.
([a-z]+) matched alleen letters.
Spaties tussen '$beker' en '=', en tussen '=' en '"' matchen
\s{0,} Matched 0 of meer spaties (of andere witruimte karakters).
De quotes
["|\']? matched 0 of 1 dubbele quote, of 0 of 1 enkele quote
De puntkomma
Het ';' matches vanzelfspreken het ';' teken, en sluit de variabele af.
Probeer nu voor de grap $beker eens te vervangen door $kan. Of vervang "koffie" eens door "thee".
Deze lastige nog een keer, maar dan met preg_match
De volgende code zal het zelfde weergeven als het vorige voorbeeld
- <?php
- echo 'De '.$matches[1].' is gevuld met '.$matches[2].'.';
- ?>
Maar wat nu als we meerdere variabelen willen gebruiken? Een voorbeeld:
- <?php
- preg_match('/\$([a-z]+)\s{0,}=\s{0,}["|\']?([a-z]+)["|\']?;/is', '$beker = "koffie";$kan = "thee";', $matches);
- ?>
resulteert:
- (
- [0] => $beker = "koffie";
- [1] => beker
- [2] => koffie
- )
Waar is dan die kan thee gebleven? Hiervoor kunnen we preg_match_all gebruiken.
- <?php
- preg_match_all('/\$([a-z]+)\s{0,}=\s{0,}["|\']?([a-z]+)["|\']?;/is', '$beker = "koffie";$kan = "thee";', $matches);
- ?>
Resulteert:
Om deze gegevens nu te gebruiken, kunnen we bijvoorbeeld doen:
- <?php
- preg_match_all('/\$([a-z]+)\s{0,}=\s{0,}["|\']?([a-z]+)["|\']?;/is', '$beker = "koffie";$kan = "thee";', $matches);
- foreach($matches[0] as $key => $value) {
- echo 'De '.$matches[1][$key].' is gevuld met '.$matches[2][$key].'.<br />';
- }
- ?>
En je ziet verschijnen:
Plain | Plain new window | code:
- De beker is gevuld met koffie.
- De kan is gevuld met thee.
Een functie gebruiken om iets te doen met de matches
Het kan soms handig zijn om een functie te gebruiken om iets te doen met de matches. Ik gebruik hiervoor weer dezelfde regex. We gaan nu gebruik maken van preg_replace_callback.
- <?php
- preg_replace_callback('/\$([a-z]+)\s{0,}=\s{0,}["|\']?([a-z]+)["|\']?;/is', 'callback_functie', '$beker = "koffie";$kan = "thee";');
- function callback_functie($matches) {
- echo 'De '.$matches[1].' is gevuld met '.$matches[2].'.<br />';
- }
- ?>
Dit heeft hetzelfde resultaat als het vorige voorbeeld. Bij OOP werkt dit iets anders. Op de plek van 'callback_functie' komt dan een array met $this erin, en de functie naam. Een klein voorbeeld:
- <?php
- class foo {
- function bar() {
- return preg_replace_callback('/\$([a-z]+)\s{0,}=\s{0,}["|\']?([a-z]+)["|\']?;/is', array($this, 'callback_functie'), '$beker = "koffie";$kan = "thee";');
- }
- function callback_functie($matches) {
- return 'De '.$matches[1].' is gevuld met '.$matches[2].'.<br />';
- }
- }
- $foo = new foo();
- echo $foo->bar();
- ?>
Tot slot
Dit was erg veel. Ik raad aan om gewoon te experimenteren hiermee. En natuurlijk hoef je het niet in een keer uit je hoofd te weten, je kan altijd even terug kijken naar hoe het ook alweer zat. Wellicht dat ik de tutorial nog iets ga aanpassen om eventuele fouten te corrigeren, of dingen beter uit te leggen.
Succes!
Lees de forum regels
