<< Tilbage

Indhold

Regulære udtryk
   Hvad er det?
   Første eksempel
   Introduktion til metacharacters
      ^   Starten af en streng
      $   Slutningen af en streng
      |   Alternation (det ene eller det andet)
      []  Character classes
      [^  Negated character classes
      .   Punktum
      \   Backslashes
   Quantifiers
      *   Stjernen
      +   Plus
      ?   Spørgsmåltegn
      {}  Tuborgklammer
   Forkortelser
   Opsummering
   Fang indhold med parenteser
   Php's regex funktioner
          Preg_match
          Preg_replace
   Avanceret regex
          Søg case-insensitive
          /e-modifieren


Regulære udtryk

Hvad er det?

Regulære udtryk (regular expressions eller regex), er ekstremt nyttige når man taler om validering. Validering vil sige at man kontrollere at de informationer man modtager er gyldige. F.eks. om email-adresser, telefonnumre, datoer, urls er gyldige. Man kan altså bl.a. bruge det til at undersøge om strenge har et bestemt format/form på en meget fleksibel måde. Derudover kan man hive komplekse dele af en streng ud, eller erstatte dele med noget andet.

Der findes mange forskellige varianter af regulære udtryk. I denne tutorial benytter vi os af perl-kompatible udtryk (PCRE), som er det mest udbredte. I PHP giver det mulighed for bl.a. at benytte funktionerne preg_match() og preg_replace().

Første eksempel

Vi starter ud med et simpelt eksempel:
/hejsa/
Denne regex matcher en streng der indeholder ordet hejsa. De 2 skråstreger kaldes delimiters og angiver i princippet bare hvor udtrykket starter og stopper. I stedet for skråstreger kunne jeg også have benyttet andre tegn (f.eks. #hejsa#), men det skal være det samme tegn der starter og slutter regex'en.

Det er vigtigt lige at huske på, at det regulære udtryk søger inde i en tekst (streng) hvor den forsøger at finde/matche noget der svarer til udtrykket. Ovenstående eksempel ville f.eks. matche 'blahh hejsa blahh'

Introduktion til metacharacters

Metacharacters er tegn som har en bestemt betydning i et regulært udtryk. Det er disse tegn der gør regulære udtryk så kraftfulde, for hvis man bare ønsker at søge efter et ord i en streng, så er der meget simplere metoder til dette formål.

Starten af en streng

/^hejsa/
Et ^ tegn betyder groft sagt "starten af strengen". Dvs. eksemplet ovenfor forsøger at matche en streng der starter med h, efterfulgt af e, j, s, og a. Det kunne eksempelvis være: hejsa hvordan går det.
^ er et metacharacter, og har altså en helt bestemt betydning, nemlig starten af strengen.

Slutningen af en streng

/hejsa$/
Her er slutnigen af strengen angivet med et dollartegn (har intet med variabler at gøre som vi normalt bruger dollartegnet til i php). Eksemplet vil matche f.eks.: jamen hejsa og hejsa.

Alternation

Alternation giver mulighed for at matche det ene eller det andet. Eksempel:
/^(hej|goddag|velkommen)/
Her matcher vi en streng der starter med enten hej eller goddag eller velkommen. De forskellige muligheder er adskilt med et | tegn. Både hejsa hvordan går det, goddag du og velkommen til min side vil kunne bruges.

Her er et eksempel mere hvor vi gerne vil finde ud af om strengen indeholder navnet Michael. Men Michael kan jo både staves med ch og k, og vi vil gerne kunne tillade begge:

/mi(ch|k)ael/

Bemærk hvordan vi bruger parentesen til at afgrænse.

Character classes

Med en character class ([..]) kan man fortælle hvilke tegn/tal/bogstaver der er tilladte på en bestemt position i en streng. Eksempel:
/a[cbah]/
Her forsøger den at matche en streng der indeholder et a efterfulgt af et af de tegn som står i klammen (f.eks. ab, aa eller ah). Rent praktisk har ovenstående eksempel samme betydning som /a(c|b|a|h)/

Indholdet af klammen er altså de ting der tilladte på præcis den plads. En smart feature ved en character class er, at man kan benytte en sekvens (f.eks. fra 0 til 9 eller a til z). Eksempel:

/5[a-z0-9]/
Her matcher vi et 5 tal efterfulgt af et bogstav fra a til z eller et tal fra 0 til 9. Bemærk at den som standard søger case-sensitive, dvs. at a-z ikke tillader store bogstaver. Her er endnu et eksempel hvor vi både tillader store bogstaver, tal, samt de danske tegn æ, ø og å:
/^[a-zA-Z0-9æøåÆØÅ]/
(Matcher en streng der starter med et af de tegn vores character class tillader)

Negated character classes

I afsnittet ovenover kiggede vi på hvordan man med en character class kan tillade bestemte tegn, på en bestemt plads. Men kan også angive hvilke tegn man IKKE ønsker. Dette gøres ved at tilføje et ^ tegn i starten af character klassen:
/^[^0-9]/
Matcher en streng der IKKE starter med et tal mellem 0 og 9. Bemærk at tegnet ^ udenfor klammerne betyder starten af strengen mens ^ indenfor klammerne fortæller hvilke ting vi ikke ønsker.

Punktum

Et punktum er også et metacharacter, som man bruger til at matche hvilket som helst tegn med. Eksempel:
/^a.../
Her matcher vi en streng der starter med a efterfulgt af hvilket som helst tegn tre gange (og ikke kun a... men også a-B9, a123 osv.). Punktummet repræsenterer i princippet alt.

Backslashes fjerner betydningen

Vi har set på hvordan bestemte tegn har en helt bestemt betydning i et regulært udtryk. Men man kan selvfølgelig også komme ud for at skulle bruge disse tegn (f.eks. ".com" uden at punktummet betyder hvilket-som-helst-tegn). Der problem kan man løse ved at tilføje en backslash:
/\.dk$/
Her matcher vi en streng der slutter med ".dk". Bemærk at vi har tilføjet en backslash til punktummet, så den mister betydningen hvilket-som-helst-tegn. Dette kan man også gøre med alle andre metacharacters.

Quantifiers

Quantifiers bruges til at bestemme hvor mange gange f.eks. et tal skal optræde. Der er grundlæggende set 4 metoder at gøre det på:

Stjernen

Man bruger stjernen/asterisk til at matche noget bestemt 0 ellere flere gange. Eksempel:

/^[0-9]*/
Matcher et tal fra 0-9 så mange gange som muligt, i starten af en streng. Det kunne f.eks. være: '112' i strengen 112 er nummeret på alarmcentralen.

Quantifiers (her stjernen) bruges altså til at bestemme hvor mange gange, at det der står foran skal optræde.

Plus

Plus (+) minder meget om stjernen, bortset fra at her skal det foranstående optræde minimum én gang. Eksempel:
/.+/
Her matcher vi en streng der indeholder hvilket som helst tegn (punktummet) 1 eller flere gange. Der kan i princippet være alle strenge, bare de ikke er tomme.

Der er intet i vejen for at benytte en parentes foran en quantifier:
/([0-9] )+/
Matcher et tal efterfulgt af et mellem, én eller flere gange. Det kunne være '3 2 6 7 ' eller bare '1 '

Spørgsmålstegn

Når man bruger spørgsmålstegnet skal det foranstående optræde 0 eller 1 gang. Eller rettere: optræder MÅSKE:

/([0-9] ?)+/
Et tal, måske efterfulgt af et mellemrum, 1 eller flere gange. Det kunne være: '123', '12 3' eller '1 2 3'

Tuborgklammer

Med tuborgklammer kan vi mere præcist angive hvor mange gange det foranstående skal optræde. Eksempel:

/^[0-9]{2,5}$/
Matcher en streng der starter med et tal, som optræder mellem 2 og 5 gange, og så slutter strengen. Det kunne være '12345', '12' eller '6421'.

Man kan også fortælle den at vi som minimum vil have det til at optræde et bestemt antal gange, uden noget øvre loft:

/^[0-9]{5,}$/
Matcher en streng der starter med et tal, der optræder 5 gange til uendelig, og så slutter strengen. Det kunne være '1234567897545' eller '98765'.

Forkortelser

Der findes nogle smarte forkortelser man kan benytte. Her er en tabel med de mest nyttige:
Forkortelse Beskrivelse Eksempel
\d I stedet for at skrive [0-9] kan man bruge \d (digit) /^\d\d$/
Matcher en streng på 2 tal.
\D Alt der IKKE er et tal. Svarende til [^0-9] /^\D*$/
Matcher en streng der indeholder alt andet end tal.
\s Matcher space/mellemrum, incl. bl.a. tabs (\t) og newlines (\n) /[a-z]\s[a-z]/
Et bogstav, efterfulgt af et mellemrum (fx et tab), og endnu et bogstav.
\S Alt andet end mellemrum, svarende til [^\s]  
\w Matcher et "bogstav", svarende til [a-zA-Z0-9_] /^\w+/
Strengen starter med et bogstav, 1 eller flere gange
\W Svarer til [^a-zA-Z0-9_], alt andet end et bogstav  
\b Den her er smart, og matcher et såkaldte word-boundary. Forestil dig at vi skal matche et helt ord, hvordan definere man så hvornår ordet starter og slutter? Nogle gange er der et space foran og bagved, andre gange et linjeskift, og hvad med starten af en streng? Det tager \b sig af. /\b[a-zæøå]+\b/
Matcher et ord i en streng. \b markerer starten og slutningen på et ord.

Opsummering

Her er en lille opsumering på hvad vi har lært hidtil:

Udtryk Betydning
^ Starten af en streng
$ Slutningen af en streng
| Enten det ene eller det andet
[..] En character-class. Hvad er tilladt på en bestemt plads?
[^..] En negativ character-class. Hvad er ikke tilladt.
. Hvilket som helst tegn.
* Matcher det foranstående 0 eller flere gange
+ Én eller flere gange
? Det foranstående optræder måske.
{start,stop} Det foranstående optræder minimum start gange og maksimum stop gange.
\d Et tal.
\s Space, et mellemrum (tab, newline...osv)


Fang indhold med parenteser

En af de ting der gør regulære udtryk så kraftfulde, er muligheden for at trække indhold ud af en streng, og ikke bare finde ud af om den matcher eller ej. Og det gør man ved at benytte parenteser omkring de ting man gerne vil have fat i.

Forestil dig en streng der indeholder '#2=Jens Hansen' og vi så hive tallet ud, samt navnet. Dette kan man gøre på følgende måde:

/^#([0-9]+)=(.*)$/

To gange bruger vi en parentes. Først omkring tallet, [0-9]+, og dernæst omkring alt hvad der kommmer efter lighedstegnet. Det gør så at vi f.eks. vha. PHP kan få et array ud med indholdet af parenteserne (plads 1 indeholder tallet 2 og plads to indeholde navnet).

Et eksempel mere... forestil dig at vi har en email-adresse 'test@testsiden.dk', og gerne vil trække det der står før snabel-a ud, og det der står efter:

/([^@]+)@(.+)/

[^@]+ betyder alt andet end et snabel-a, 1 eller flere gange. Det vil gå med t, e, s og t, men når den så kommer til @, så stopper den og går videre. Så kommer der et snabel-a, og bagefter hvilket som helst tegn, 1 eller flere gange. De to dele jeg ønsker at trække ud har jeg sat parentes omkring. Lige om lidt ser vi på hvordan det rent praktisk foregår med php.

Php's regex funktioner

Indtil nu har vi kigget på regulære udtryk generelt. Det begrænser sig på ingen måde til php, og kan derfor bruges i en lang række sprog. Nu skal vi se på hvordan man i php kan benytte sig af regulære udtryk.

preg_match()

preg_match() funktionen bruges, som navnet nok antyder, til at matche en streng mod et regulært udtryk. Eksempel:

Kode:
1.
2.
3.
4.
5.
6.
7.
<?php
$postnr 
"2860";
if (
preg_match("/^[0-9]{4}$/"$postnr))
{
    echo 
"Strengen blev matchet, et gyldigt dansk postnummer";
}
?>
Resultat:
Strengen blev matchet, et gyldigt dansk postnummer

Det første argument i funktionen indeholder selve det regulære udtryk, mens det andet argument indeholder den streng vi gerne vil kigge i. Funktionen returnerer true eller false afhængig af om det lykkedes.

preg_match() kan også bruges til at trække data ud. Lad os prøve et af de tidligere eksempler med e-mail adressen:

Kode:
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
<?php
$email 
"test@testsiden.dk";
if (
preg_match("/([^@]+)@(.+)/"$email$regs))
{
    echo 
"Strengen blev matchet, og indeholder følgende:<br/>";
    echo 
$regs[0] . "<br/>"//Alt hvad der blev matchet
    
echo $regs[1] . "<br/>"//Parentes 1
    
echo $regs[2];           //Parentes 2
}
?>
Resultat:
Strengen blev matchet, og indeholder følgende:
test@testsiden.dk
test
testsiden.dk

Læg mærke til at der er kommet et ekstra parameter på preg_match() funktionen. Det er $regs som kommer til at indeholde det vi har angivet i parenteserne. $regs[0] indeholder alt hvad der er blevet matchet, $regs[1] indeholder første parentes og $regs[2] anden parentes.

preg_replace()

Du har sikkert prøvet at skrive et link på et forum, hvorefter linket er blevet gjort klikbart. Det er højst sansynligt et regulært udtryk man har benyttet, hvor man erstatter selve linket med linket puttet ind i et a-tag. preg_replace() erstatter ét med noget andet, vha. et regulært udtryk, i en streng.

Her er et eksempel hvor vi fremhæver alle tal i en streng:

Kode:
1.
2.
3.
4.
5.
<?php
$str 
"Jeg er 123 år gammel";
$str preg_replace("/\d+/""<b>\\0</b>"$str);
echo 
$str;
?>
Resultat:
Jeg er 123 år gammel

Det første argument indeholder det regulære udtryk for hvad der skal erstattes, det næste argument hvad det skal ersattes til og det sidste argument hvori det skal ske. Læg mærke til at vi benytter \\0 til at få fat i indholdet af det udtrykket har matchet. Det fungerer lidt på samme måde som med parenteser. \\0 indeholder hele udtrykket, \\1 indeholder den første parentes osv. Her er et eksempel mere hvor vi benytter parenteser:

Kode:
1.
2.
3.
4.
5.
<?php
$str 
"test@testsiden.dk";
$str preg_replace("/([^@]+)@(.*)/""<i>\\1</i>@<b>\\2</b>"$str);
echo 
$str;
?>
Resultat:
test@testsiden.dk

Den første parentes fanger det der står før snabel-a, og det erstatter vi med <i>\\1</i> hvor \\1 selvfølgelig svarer til indholdet af den første parentes. På samme måde gør vi det der står efter snabel-a fed.

Avanceret regex

Søg case-insensitive

Det er ikke altid at man med sikkerhed kan sige, hvordan strengen man forsøger at matche er bygget op. Som standard er et regulært udtryk case-sensitive, dvs. der er forskel på store og små bogstaver. Forestil dig at du har en HTML kode hvor du vil finde ud af hvad titlen er (title tag'et). Det kan både være <title>test</title>, <TITLE>test</TITLE> og <TiTlE>test</TiTlE>. Derfor duer det ikke med /<title>([^<]*)<\/title>/.

I stedet er det nødvendigt at tilføje en såkaldt modifier. Det er et bogstav man sætter efter den afsluttende delimiter (den sidste skråstreg). I dette tilfælde, hvor vi vil søge case-insensitivt, er det bogstavet 'i' vi skal have fat i. Eksempel:

Kode:
1.
2.
3.
4.
5.
<?php
$html 
"Noget html kode <TiTlE>dette er min titel</TiTlE> noget andet her";
preg_match("/<title>([^<]*)<\/title>/i"$html$regs);
echo 
$regs[1];
?>
Resultat:
dette er min titel

Havde vi ikke sat det lille i-bogstav efter den sidst skråstreg, så havde udtrykket ikke matchet noget, da "title" ikke er det samme som "TiTlE".

/e-modifieren

Normalt når man bruger preg_replace() til at erstatte, så erstatter vi et udtryk med en streng. e-modifieren giver mulighed for at erstatte et udtryk med en streng som bliver evalueret af php, med andre ord: vi kan bruge php kode. Det illustreres meget godt af følgende eksempel:

Kode:
1.
2.
3.
4.
<?php
$str 
"Denne simplificerede test viser e modifieren";
echo 
preg_replace("/\w{7,}/e""strtoupper('\\0')"$str);
?>
Resultat:
Denne SIMPLIFICEREDE test viser e MODIFIEREN

I eksemplet erstattes ord på mindst 7 tegn med det samme ord bare i uppercase. Bemærk at den udføre det andet argument (strtoupper("\\1")) som PHP kode. Dette kan lade sig gøre ved brug af e-modifieren.

Der er visse ricisi forbundet ved at gøre det på denne måde, så brug det med omtanke (læs herunder).

Leder du efter billige usb stik?, så kan du få et Billigt USB stik hos usb.dk

Spørgsmål/forslag/kommentarer rettes til webmaster@phpartikler.dk

Billige USB stik