Terugverwijzingen
Bij gebrek aan een betere term heb ik "backreferences" vertaald als "terugverwijzingen". Als iemand een term heeft die beter bekt, dan hoor ik dat graag.
Het groeperen heeft een uiterst nuttig neveneffect. Dat komt omdat bepaalde regex-implementaties de in een groep gepaste tekst "onthouden", en deze beschikbaar stellen, tijdens en soms zelfs na het toepassen van de regex.
Neem eens aan dat je in een stuk tekst op zoek bent naar dubbele woorden, zoals …dus•dus…. Nou zou je kunnen proberen om een aparte regex te schrijven voor elk woord dat je maar kan bedenken, maar zou het niet makkelijk zijn als je zoiets kon zeggen als, "zoek iets wat dit patroon past, en pas het dan nog eens"?
Dat kan. Vooropgesteld dat de regex implementatie het ondersteunt, "onthouden" haakjes ((…)) wat ze passen. In dat geval zou je op dubbele woorden kunnen zoeken met de regex ([a-zA-Z]+)\1. Het metakarakter \1 wordt een terugverwijzing (backreference) genoemd.
Deze regex past echter ook gevallen als dus dusdanig, daarom is in dit geval ([a-zA-Z]+)•\1\> een betere regex.
Om te bepalen welke terugverwijzing bij welke groep hoort, dien je het aantal openingshaakjes te tellen vanaf links. In bovenstaand voorbeeld was maar één groep in het spel, dus dat is makkelijk. Bij het volgende voorbeeld ligt het wat ingewikkelder. ((de|een) (grote( rode)?|kleine( gele)?) (auto|fiets)) bevat zes groepen. Het voorbeeldbestand (auto.txt) bestaat uit vijf regels, waarvan er vier door de regex gepast kunnen worden:
$ egrep '((de|een) (grote( rode)?|kleine( gele)?) (auto|fiets))' auto.txt
de grote rode auto
een kleine fiets
de kleine gele auto
een grote rode fiets
Om het wat duidelijker te maken welke terugverwijzing bij welke groep hoort, heb ik een perl-scriptje geschreven. Dit geeft de volgende uitvoer:
$ perl -n refs.pl auto.txt
"de grote rode auto"
\1 => de grote rode auto
\2 => de
\3 => grote rode
\4 => rode
\5 => (null)
\6 => auto
"een kleine fiets"
\1 => een kleine fiets
\2 => een
\3 => kleine
\4 => (null)
\5 => (null)
\6 => fiets
"de kleine gele auto"
\1 => de kleine gele auto
\2 => de
\3 => kleine gele
\4 => (null)
\5 => gele
\6 => auto
"een grote rode fiets"
\1 => een grote rode fiets
\2 => een
\3 => grote rode
\4 => rode
\5 => (null)
\6 => fiets
Het scriptje past de regex op elke regel van het voorbeeldbestand toe, en drukt de terugverwijzingen af als de regex de regel past. Hierbij geeft (null) aan dat de groep waar de terugverwijzing op terugslaat geen deel uitmaakt van de match.
Je kan dus meerdere groepen in een regex gebruiken, maar het maximum aantal terugverwijzingen is in de meeste regex-implementaties beperkt tot negen (\1 ... \9).
Een wat groter voorbeeld is het ontrafelen van de query string in een URL, zoals bijvoorbeeld http://www.foobar.com/search?query=regex&view=detailed.
Neem nou eens aan dat we de naam en waarde van de query variabele uit deze URL willen hebben. Dit kan gedaan worden met de regex \?([a-zA-Z]+)=(^&]+). In deze regex gebruiken we \? om de regex uit te lijnen op de query gedeelte, welke direct na het vraagteken volgt. Vervolgens passen we de naam van een variabele met [a-zA-Z]+, en zetten het tussen haakjes om het voor later gebruik op te slaan, ([a-zA-Z]+). Dit wordt gevolgd door een is-gelijk teken, dus voegen we = toe aan de regex. Tenslotte moeten we de waarde van de variabele "vangen". Dit kan met [^&]+, aangezien de string waaruit de waarde bestaat doorloopt tot de volgende &, welke dienst doet als scheidingsteken in de naam=waarde reeks. Dit werkt ook als de waarde niet gevolgd wordt door een ampersand, want in dat geval is de rest van de waarde van de variabele gewoon de rest van de URL. De waarde-regex moet tussen haakjes worden gezet aangezien we de waarde willen opslaan voor later, dus krijgen we ([^&]+).
Hoewel er twee paar haakjes in de regex staan, wordt geen van beide in de regex als terugverwijzing gebruikt. Hoe komen we dan bij de data? Nou, dat hangt sterk af van de tool waarin de regex wordt gebruikt. Hier volgen wat voorbeelden.
Met perl is de inhoud van beide terugverwijzingen na het passen beschikbaar in de variabelen $1 en $2. Het volgende stukje code laat zien hoe je dat kunt gebruiken.
$url = 'http://www.foobar.com/search?query=regex&view=detailed';
$url =~ /\?([a-zA-Z]+)=([^&]+)/;
print "$1 = $2\n";
In PHP dien je de verzameling van ereg()-functies te gebruiken (zie de handleiding), zoals bijvoorbeeld:
$url = 'http://www.foobar.com/search?query=regex&view=detailed';
ereg('\?([a-zA-Z]+)=([^&]+)', $url, $refs);
echo "$refs[1] = $refs[2]\n";
Inhoudsopgave
- Inleiding
- Wat zijn het?
- Metakarakters
- Karakterklassen
- De Punt
- Kwantificeerders
- Alternatie
- Groeperen
- Terugverwijzingen
- Ten slotte