&Anders.Lund; &Anders.Lund.mail;
&Otto.Bruggeman;&Freek.de.Kruijf;

# Reguliere expressies

    Deze Appendix bevat een beknopte maar hopelijk voldoende en
    dekkende introductie tot de wereld van reguliere
    expressies. Het documenteert reguliere expressies in de vorm
    beschikbaar in &kappname;, wat niet compatibel is met de reguliere
    expressies van perl, noch met die van bijvoorbeeld
    grep.

## Inleiding

Reguliere expressies bieden ons een manier om mogelijke inhoud van een tekenreeks te beschrijven zodat deze verstaan wordt door een klein stukje software, waarmee het kan nagaan of een tekst overeenkomt en ook in het geval van geavanceerde toepassingen met de middelen om stukjes overeenkomende tekst op te slaan.

Een voorbeeld: Zeg dat u naar een tekst in paragrafen wilt zoeken die begint met ofwel de namen Hendrik of Piet gevolgd door een werkwoordsvorm zegt.

Met een normale zoekopdracht, zou u beginnen met te zoeken naar de eerste naam, Hendrik misschien gevolgd door ze zoals dit: `Hendrik ze` en, terwijl u zoekt naar overeenkomsten, moet u diegene negeren die niet het begin van een paragraaf zijn, evenals die waarin het woord beginnend met de letters ze niet ofwel zegt, zei of zoiets is. En dan natuurlijk dat alles herhalen met de volgende naam...

Met reguliere expressies kan die taak worden gedaan met een enkele zoekopdracht en met een grotere graad van precisie.

Om dit te bereiken definieert Reguliere Expressies regels voor het uitdrukken van details op een generaliserende manier van een overeenkomst met een tekenreeks. Ons voorbeeld, die we letterlijk als volgt uitdrukken: Een regel beginnend met ofwel Hendrik of Piet (mogelijk gevolgd door tot 4 spaties of een tab) gevolgd door een witruimte gevolgd door ze en dan ofwel gt of i kan worden uitgedrukt met de volgende reguliere expressie:

`^[ \t]{0,4}(Hendrik|Piet) ze(gt|i)`

Het bovenstaande voorbeeld demonstreert alle vier belangrijkste concepten van moderne reguliere expressies namelijk:

- Patronen

- Toekenningen

- Aantallen

- Achterwaartse verwijzingen

Het dakje (`^`) waarmee de uitdrukking begint is een toekenning, die alleen waar is als de volgende overeenkomende tekenreeks zich aan het begin van de regel bevindt.

De tekenreeksen `[ \t]` en `(Hendrik|Piet) ze(gt|i)` zijn patronen. De eerste is een tekenklasse die ofwel overeenkomt met een spatie of een teken (horizontale) tab; het andere patroon bevat eerst een subpatroonn overeenkomend met ofwel `Hendrik` of `Piet`, daarna iets wat exact overeenkomt met de tekenreeks `ze` en tenslotte een subpatroon overeenkomend met ofwel `gt` of `e`

De tekenreeks `{0,4}` is een aantal dat aangeeft ergens van 0 t/m 4 van de vorige.

Omdat software voor reguliere expressies, die het concept van achterwaartse verwijzingen ondersteunt, het gehele deel van de tekenreeks, dat overeenkomt, evenals subpatronen tussen haakjes, opslaat, geeft dus ook toegang tot die verwijzingen, zodat we onze handen kunnen leggen op ofwel de gehele overeenkomst (bij het zoeken in een tekstdocument in een tekstbewerker met een reguliere expressie, die vaak gemarkeerd is als geselecteerd) of ofwel de gevonden naam of het laatste deel van de zin.

Samenvattend zal de uitdrukking overeenkomen met wat we willen waarmee deze moet overeenkomen en alleen daarmee.

De volgende secties zullen in detail beschrijven hoe patronen, tekenklassen, toekenningen, aantallen en achterwaartse verwijzingen, te construeren en te gebruiken en de laatste sectie zal een paar bruikbare voorbeelden geven.

## Patronen

Patronen bestaan uit letterlijke tekenreeksen en tekenklassen. Patronen mogen subpatronen bevatten, die patronen zijn tussen haakjes.

### Escape-tekens

In patronen evenals in tekenklassen hebben sommige tekens een speciale betekenis. Om zulke tekens letterlijk overeen te laten komen met deze tekens, moeten ze zo gemarkeerd worden of escaped om de software voor reguliere expressies te laten weten dat het zulke tekens letterlijk moet interpreteren.

Dit wordt gedaan door het teken vooraf te laten gaan door een backslash (`\`).

De software voor reguliere expressies zal stilletjes een teken met een escape-teken ervoor negeren die geen speciale betekenis heeft in de context, dus, bijvoorbeeld, een j met een escape ervoor (`\j`) is veilig. Als u er niet zeker van bent of een teken een speciale betekenis heeft, dan kunt u er veilig een escape-teken voor zetten.

Met het escape-teken werken omvat natuurlijk ook het teken backslash zelf, om letterlijk dat teken weer te geven, moet u schrijven `\\`.

### Tekenklassen en afkortingen

Een tekenklasse is een uitdrukking die overeenkomt met een gedefinieerde set tekens. In Reguliere Expressies, worden tekenklassen gedefinieerd door de toegestane tekens voor de klasse tussen rechte haken te zetten, `[]`, of door een van de hieronder beschreven afgekorte klassen te gebruiken.

Eenvoudige tekenklassen bevatten letterlijk een of meer tekens, bijvoorbeeld `[abc]` (komt overeen met een van de letters a, b of c) of `[0123456789]` (komt overeen met een cijfer).

Omdat letters en cijfers een logische volgorde hebben, kunt u deze afbreken door er reeksen van te specificeren: `[a-c]` is gelijk aan `[abc]` en `[0-9]` is gelijk aan `[0123456789]`. Combineren van deze constructies, bijvoorbeeld `[a-fynot1-38]` is geheel toegestaan (de laatste zou natuurlijk overeenkomen met ofwel a,b,c,d, e,f,y,n,o,t,1,2,3 of 8).

Omdat hoofdletters andere tekens zijn dan hun kleine letter equivalenten, is het nodig om overeenkomsten zonder onderscheid van hoofd- en kleine letters te maken tussen a of b, moet u het schrijven als `[aAbB]`.

Het is natuurlijk mogelijk om een negatieve klasse overeenkomst te maken zoals alles behalve. Er moet dan een dakje (`^`) aan het begin van de klasse staan:

`[^abc]` komt overeen met elk teken behalve a, b of c.

Naast letterlijke tekens, zijn er ook enige afkortingen gedefinieerd, om het leven een beetje gemakkelijker te maken:

`\a`

  Dit komt overeen met het ASCII bel-teken (BEL, 0x07).

`\f`

  Dit komt overeen met het ASCII formfeed-teken (FF, 0x0C).

`\n`

  Dit komt overeen met het ASCII linefeed-teken (LF, 0x0A, Unix nieuwe-regel).

`\r`

  Dit komt overeen met het ASCII carriage-return-teken (CR, 0x0D).

`\t`

  Dit komt overeen met het ASCII horizontale tab-teken (HT, 0x09).

`\v`

  Dit komt overeen met het ASCII verticale tab-teken (VT, 0x0B).

`\xhhhh`

  Dit komt overeen met het Unicode-teken overeenkomend met het hexadecimale getal hhhh (tussen 0x0000 en 0xFFFF). \0ooo (&ie;, \zero ooo) komt overeen met het ASCII/Latin-1 teken overeenkomend met het octale getal ooo (tussen 0 en 0377).

`.` (punt)

  Dit komt overeen met elk teken (inclusief nieuwe-regel).

`\d`

  Dit komt overeen met een cijfer. Gelijk aan `[0-9]`

`\D`

  Dit komt overeen met een niet-cijfer. Gelijk aan `[^0-9]` of `[^\d]`

`\s`

  Dit komt overeen met een witruimte-teken. Praktisch gelijk aan `[ \t\n\r]`

`\S`

  Dit komt overeen met een niet-witruimte-teken. Praktisch gelijk aan `[^ \t\r\n]` en gelijk aan `[^\s]`

`\w`

  Komt overeen met elk woordteken - in dit geval elke letter, elk cijfer of underscore. Gelijk aan `[a-zA-Z0-9_]`

`\W`

  Dit komt overeen met een niet-woord-teken - alles behalve letters, cijfers of underscore. Gelijk aan `[^a-zA-Z0-9_]` of `[^\w]`

De POSIX-notatie van klassen, `[:<class name>:]` wordt ook ondersteund. Bijvoorbeeld, `[:digit:]` is equivalent met `\d` en `[:space:]` met `\s`. Zie de volledige lijst met POSIX-tekenklassen hier.

De afgekorte klassen kunnen in een zelf gemaakte klasse worden gezet, bijvoorbeeld om overeen te komen met een woord-teken, een spatie of een punt, u kunt dan schrijven `[\w \.]`

#### Tekens met een speciale betekenis in tekenklassen

De volgende tekens hebben een speciale betekenis binnen de constructie [] van de tekenklasse en er moet een escape-teken voor om letterlijk ingevoegd te worden in een klasse:

`]`

  Beëindigt de tekenklasse. Moet een escape-teken voor tenzij het het allereerste teken in de klasse is (mag na een dakje zonder escape-teken)

`^` (dakje)

  Geeft een negatieve klasse aan als het het eerste teken is. Moet een escape-teken voor om letterlijk overeen te komen als het het eerste teken in de klasse is.

`-` (streepje)

  Geeft een logische reeks aan. Moet altijd een escape-teken voor binnen een tekenklasse.

`\` (backslash)

  Het escape-teken. Moet altijd een escape-teken voor.

### Alternatieven: overeenkomen met één van

Als u met een set van alternatieve patronen overeen wilt komen, dan kunt u die scheiden door `|` (teken verticale streep).

Om bijvoorbeeld ofwel Jan of Harry te zoeken, zou u een expressie `Jan|Harry` moeten gebruiken.

### Subpatronen

Subpatronen zijn patronen omgeven door haakjes, ze kunnen op verschillende manieren in de wereld van de reguliere expressies worden gebruikt.

#### Alternatieven specificeren

U kunt een subpatroon gebruiken om een set alternatieven in een groter patroon te groeperen. De alternatieven zijn gescheiden door het teken | (verticale streep).

Om de woorden int, float of double te laten overeenkomen, kunt u het patroon `int|float|double` gebruiken. Als u het alleen wilt vinden wanneer het gevolgd wordt door enige witruimte en dan enige letters, stop de alternatieven dan in een subpatroon: `(int|float|double)\s+\w+`.

#### Overeenkomende tekst vangen (achterwaartse verwijzingen)

Als u een achterwaartse referentie wilt gebruiken, gebruik dan een subpatroon `(PATTERN)` om het gewenste deel van het patroon te onthouden. Om te verhinderen het subpatroon te onthouden, gebruik een non-capturing groep `(?:PATTERN)`.

U wilt bijvoorbeeld twee exemplaren van hetzelfde woord gescheiden door een komma en mogelijk enige witruimte, dan kunt u schrijven `(\w+),\s*\1`. Het subpatroon `\w+` zou een groep tekens in een woord vinden en de gehele expressie zou overeenkomen als die gevolgd worden door een komma, 0 of meer witruimtes en dan een gelijke groep woordtekens. (De tekenreeks `\1` refereert naar het eerste subpatroon ingesloten in haakjes)

Om meerduidigheid met het gebruik van `\1` met enige cijfers erachter (&eg; `\12` kan het 12de subpatroon zijn of gewoon het eerste subpatroon met `2`) we gebruiken `\{12}` als syntaxis voor subpatronen met meerdere cijfers.

Voorbeelden:

- `\{12}1` is gebruik subpatroon 12

- `\123` is gebruik gevangen 1 daarna 23 als de normale tekst

#### Vooruitblik-toekenningen

Een vooruitblik-toekenning is een subpatroon die begint met ofwel `?=` of `?!`.

Om bijvoorbeeld een overeenkomst te vinden met de letterlijke tekenreeks Bill maar alleen als deze niet wordt gevolgd door Gates, dan kunt u deze expressie gebruiken: `Bill(?! Gates)`. (Dit vindt Bill Clinton evenals Billy the kid, maar negeert stilletjes de andere overeenkomsten.)

Subpatronen voor toekenningen worden niet gevangen.

Zie ook Toekenningen

#### Achteruitblik-toekenningen

Een achteruitblik-toekenning is een subpatroon dat begint met ofwel `?<=` of `?<!`.

Achteruitblik heeft hetzelfde effect als de vooruitblik, maar werkt achterwaarts. Om de letterlijke tekenreeks fruit overeen te laten komen maar alleen indien niet vooraf gegaan door grape, dan kunt u deze expressie gebruiken: `(?<!grape)fruit`.

Subpatronen voor toekenningen worden niet gevangen.

Zie ook Toekenningen

### Tekens met een speciale betekenis in patronen

De volgende tekens hebben een speciale betekenis binnen een patroon en er moet een escape-teken voor om letterlijk met ze overeen te komen:

`\` (backslash)

  Het escape-teken.

`^` (dakje)

  Kent het begin van de tekenreeks toe.

`$`

  Kent het einde van de tekenreeks toe.

`()` (linker en rechter haakje)

  Geeft subpatronen aan.

`{}` (linker en rechter accolade)

  Geeft numerieke aantallen aan.

`[]` (linker en rechter rechte haakje)

  Geeft tekenklassen aan.

`|` (verticale streep)

  logische OR. Scheidt alternatieven.

`+` (plusteken)

  Aantal, 1 of meer.

`*` (sterretje)

  Aantal, 0 of meer.

`?` (vraagteken)

  Een optioneel teken. Kan geïnterpreteerd worden als een aantal, 0 of 1.

## Aantallen

Aantallen biedt een reguliere expressie de mogelijkheid om een gespecificeerd aantal of reeks aantallen van ofwel een teken, tekenklasse of subpatroon.

Aantallen worden omgeven door accolades (`{` en `}`) en hebben de algemene vorm `{[minimum-aantal][,[maximum-aantal]]}`

Het gebruik wordt het beste uitgelegd door een voorbeeld:

`{1}`

  Exact 1 keer

`{0,1}`

  Nul of 1 keer

`{,1}`

  Hetzelfde, met minder werk;)

`{5,10}`

  Minstens 5 maar maximaal 10 keer.

`{5,}`

  Minstens 5 keer, geen maximum.

Verder zijn er enkele afkortingen:

`*` (sterretje)

  gelijk aan `{0,}`, elk aantal keren.

`+` (plusteken)

  gelijk aan `{1,}`, minstens 1 keer.

`?` (vraagteken)

  gelijk aan `{0,1}`, nul of 1 keer.

### Happig

Bij het gebruik van aantallen zonder maximum, is de standaard bij reguliere expressies om overeen te komen met zo veel als mogelijk van de gezochte tekenreeks, algemeen bekend staand als happig gedrag.

Moderne software voor reguliere expressies levert de middelen om happigheid uit te zetten, hoewel in een grafische omgeving het aan het interface is om toegang tot deze functie te leveren. Een zoekdialoog bijvoorbeeld, die een reguliere expressie biedt bij het zoeken, kan een keuzevakje hebben met het label Minimale overeenkomst evenals het zou moeten aangeven of happigheid het standaard gedrag.

### In voorbeelden van context

Hier zijn een paar voorbeelden van het gebruik van aantallen

`^\d{4,5}\s`

  Komt overeen met de cijfers in 1234 ga en 12345 nu, maar niet in 567 elf of in 223459 ergens

`\s+`

  Komt overeen met één of meer witruimte tekens

`(bla){1,}`

  Komt overeen met alles van blablabla en de bla in blackbird of tabla

`/?>`

  Komt overeen met /> in <closeditem/> evenals > in <openitem>.

## Toekenningen

Toekenningen bieden de mogelijkheid om een reguliere expressie alleen te laten overeenkomen onder bepaalde gecontroleerde condities.

Een toekenning heeft geen teken nodig om mee overeen te komen, in plaats daarvan onderzoekt het de omgeving voor een mogelijke overeenkomst alvorens deze toe te kennen. Bijvoorbeeld de woordgrens toekenning probeert geen niet-woord-teken ten opzichte van een woordteken op zijn positie te vinden, in plaats daarvan moet er geen woordteken zijn. Dit betekent dat de toekenning overeen kan komen met iets waar geen teken is, &ie; aan het eind van een te zoeken tekenreeks.

Sommige toekenningen hebben in werkelijkheid een patroon waarmee overeen gekomen moet worden, maar het deel van de overeenkomst met de tekenreeks behoort niet bij een deel van het resultaat van overeenkomst met de volledige expressie.

Reguliere expressies zoals hier gedocumenteerd ondersteunt de volgende toekenningen:

`^` (dakje: begin van de tekenreeks)

  Komt overeen met het begin van de tekenreeks.

  De expressie `^Peter` komt overeen met Peter in de tekenreeks Peter, hey! maar niet in Hey, Peter!

`$` (eind van de tekenreeks)

  Komt overeen met het eind van de tekenreeks.

  De expressie `you\?$` komt overeen met de laatste you in de tekenreeks You didn't do that, did you? maar nergens in You didn't do that, right?

`\b` (woordgrens)

  Komt overeen als er een woordteken is aan één kant en niet aan de andere kant.

  Dit is nuttig om woordeinden te vinden, bijvoorbeeld beide einden om een heel woord te vinden. De expressie `\bin\b` komt overeen met het aparte in in de tekenreeks He came in through the window, maar niet met het in in window.

`\B` (niet-woordgrens)

  Komt overeen waar \b dat niet doet.

  Dit betekent dat het zal overeenkomen bijvoorbeeld in woorden: De expressie `\Bin\B` komt overeen in window maar niet in integer of I'm in love.

`(?=PATROON)` (Positief vooruitkijken)

  Een vooruitkijktoekenning kijkt naar het deel van de tekenreeks die volgt op een mogelijke overeenkomst. Het positieve vooruitkijken voorkomt dat de tekenreeks overeenkomt als de tekst die volgt op de mogelijke overeenkomst niet overeenkomt met het PATROON van de toekenning, maar voorkomt dat de tekst daarmee overeenkomt en zal niet meegenomen worden in het resultaat.

  De expressie `handy(?=\w)` komt overeen met handy in handyman maar niet in That came in handy!

`(?!PATROON)` (Negatieve vooruitblik)

  Het negatieve vooruitkijken voorkomt dat de tekenreeks overeenkomt als de tekst die volgt op de mogelijke overeenkomst overeenkomt met het PATROON van de toekenning.

  De expressie `const \w+\b(?!\s*&)` zal overeenkomen met const char in de tekenreeks const char* foo terwijl het niet kan overeenkomen met const QString in const QString& bar omdat de & overeenkomt met het patroon in de negatieve vooruitkijktoekenning.

`(?<=PATROON)` (Positief achteruit kijken)

  Een achteruitkijktoekenning heeft hetzelfde effect als de vooruitkijk, maar werkt achterwaarts. Een achteruitkijktoekenning kijkt naar het deel van de vorige tekenreeks met een mogelijke overeenkomst. Het positieve achteruitkijken zal alleen overeenkomen met een tekenreeks als het voorafgegaan wordt door het PATROON van de toekenning, maar voorkomt dat de tekst daarmee overeenkomt en zal niet meegenomen worden in het resultaat.

  De expressie `(?<=cup)cake` zal overeenkomen met cake als het volgt op cup (in cupcake maar niet in cheesecake of in alleen cake).

`(?<!PATROON)` (Negatieve achteruitblik)

  Het negatieve achteruitkijken voorkomt dat de tekenreeks overeenkomt als het voorgaande deel van de zoektekst overeenkomt met het PATROON.

  De expressie `(?<![\w\.])[0-9]+` zal overeenkomen met 123 in de tekenreeksen =123 en -123 terwijl het niet kan overeenkomen met 123 in .123 of word123.

`(PATROON)` (Vanggroep)

  Het subpatroon in de haakjes wordt gevangen en onthouden, zodat het gebruikt kan worden in achterwaartse referentie. Bijvoorbeeld, de expressie `(&quot;+)[^&quot;]*\1` komt overeeen met `""""tekst""""` en `"tekst"`.

  Zie de sectie Overeenkomende tekst vangen (achterwaartse referenties) voor meer informatie.

`(?:PATROON)` (Non-capturing groep)

  Het subpatroon in de haakjes wordt niet gevangen en niet onthouden. Het heeft de voorkeur om altijd non-capturing groepen te gebruiken als de vangst niet zal worden gebruikt.