Regulære uttrykk
Fra Wikipedia, den frie encyklopedi.
Et regulært uttrykk (ofte fortkortet «regexp», «regex», «regxp» med flertallsformer «regexps» og «regexes») er en tekststreng, som etter spesielle regler beskriver hvordan tekst er bygd opp. Bruksområder for regulære uttrykk er for eksempel i programmeringsspråk for å sjekke at oppgitt inndata er gyldig (for eksempel at det er en korrekt epostadresse1), hente ut informasjon (for eksempel alle IP-adresser i en tekstfil) og endre tekst (for eksempel erstatte alle forekomster av <b>tekst</b> med <i>tekst</i>. Regulære uttrykk er også vanlig i tekstredigerere til mer avansert søking og erstatting av tekst.
De fleste høynivå programmeringsspråk (for eksempel Perl, Python, Java, PHP, …) har en eller annen form for støtte for regulære uttrykk. Særlig er Perl kjent for sin støtte for regulære uttrykk, så denne artikkelen vil (for nå) ta utgangspunkt i Perl-kompatible regulære uttrykk. Verktøy som benytter seg av regulære uttrykk er for eksempel grep (og egrep) og sed, som brukes til henholdsvis søking i og modifisering av tekst.
| Innholdsfortegnelse |
Komme i gang
Først en liten oppmuntring: Når du aldri har sett et regulært uttrykk før, kan for eksempel ting som .*\/\* (.+?) \*\/.* se ut som tegneseriebanning, og ikke akkurat noe som kan utrette noe nyttig. Fortvil ikke – det er mye enklere enn det ser ut til. :-)
Forutsetninger
Denne artikkelen har Perl som utgangspunkt, så det kan være lurt å ha Perl installert. Hvis du bruker et annet operativsystem enn Windows er det ganske sannsynlig at du allerede har det installert. Se eventuelt artikkelen om å installere programmer.
Selv om artikkelen tar utgangspunkt i Perl, vil de regulære uttrykkene (stort sett) være generelle nok til at du kan bruke de i andre språk og programmer. Perl-koden vil skrives med mål om å være så forståelig som mulig, og ikke nødvendigvis slik en Perl-programmerer faktisk ville ha skrevet.
Eksemplene vil stort sett bare vise teksten som skal undersøkes, det regulære uttrykket og om uttrykket treffer – eventuelt også hva det treffer. Noen eksempler vil vise hvordan dette kan brukes i ulike kontekster.
Regulære uttrykk vs. jokertegn
Jokertegn, som * og ? har du sikkert vært borti tidligere – for eksempel ls *.txt. * betyr hva som helst eller ingenting. ? betyr (nøyaktig) et tegn. (Bruken av ? som at et tegn må eller kan forekomme er ikke konsistent.). I noen tilfeller (f.eks. i Bash) kan du også oppgi tegnklasser, for eksempel ls [a-z]*, som lister alle filer som begynner med en liten bokstav mellom a og z.
Man skal ikke gjøre så alt for avanserte ting før jokertegn ikke er fleksibelt nok. Hva hvis du for eksempel vil ha filer som begynner med "DSC" eller "PIC" etterfulgt av et eller flere tall og som slutter med .jpg? ls DSC[0-9].jpg DSC[0-9][0-9].jpg ... PIC[0-9].jpg PIC[0-9][0-9].jpg ...? Her kommer jokertegn til kort, men et enkelt regulært uttrykk – ^(DSC|PIC)\d+\.jpg$ – gjør jobben.
Enkel sammenligning
Lagre følgende i ei tekstfil og kjør programmet med perl filnavn.
$tekst = "Hei, verden";
if($tekst =~ /verden/) {
print "Verden hilser tilbake.\n";
}
$_=$tekst;
if(/verden/) {
print "Verden ser ut til å like deg og hilser enda en gang.\n";
}
$tekst =~ s/verden/du/;
print "$tekst\n";
s/, verden/ på deg/;
print;
Eksempelet viser hvordan =~ brukes som operator og hvordan $_ kan brukes som standardvariabel når ikke annet oppgis.
Store og små bokstaver
I regulære uttrykk skilles det mellom store og små bokstaver. I Perl kan du ignorere forskjellen på store og små bokstaver ved å legge til operatoren i – for eksempel /verden/i. Andre språk – eksempelvis PHP – opererer med forskjellige funksjoner dersom uttrykket skal ta hensyn til forskjell på store og små bokstaver.
Anker – første spesialtegn
I den første sammenligningen ser vi hvordan «Hei, verden» stemmer med uttrykket verden. verden vil også stemme med ord som «verdenskrig», «undervannsverden» og «eventyrverdendyr».
Med anker kan du si at teksten må forekomme helt i begynnelsen eller helt i slutten av teksten. Dette gjøres med henholdsvis ^ og $.
| Tekst | Regulært uttrykk | Stemmer |
|---|---|---|
| Eventyrverdendyr | verden | Ja. |
| Eventyrverdendyr | ^verden | Nei. |
| Eventyrverdendyr | verden$ | Nei. |
| Verdenskrig | ^Verden | Ja. |
Kvantifikator
Kvantifikatorer brukes til å oppgi hvor mange ganger noe skal opptre. Du kan bruke paranteser til å lage underuttrykk, som du kan bruke kvantifikatorer på.
| Modifikator | Funksjon |
|---|---|
| * | Matcher ingen eller flere ganger. Ekvivalent med {0,}. |
| + | Matcher minst en gang. Ekvivalent med {1,}. |
| ? | Matcher en eller ingen ganger. Ekvivalent med {0,1}. |
| {antall} | Matcher eksakt antall ganger |
| {start,slutt} | Matcher fra start til slutt antall ganger |
| {antall,} | Matcher minst antall ganger |
| Tekst | Regulært uttrykk |
|---|---|
| 0x1234A | \d{4} |
| Shalalalala | (la){3} |
Tegnklasser
Når du ikke vet nøyaktig hva du vil treffe, men hva slags tegn, kan du du bruke tegnklasser. Tegnklasser brukes til å uttrykke "alle alfanumeriske tegn", "alle tall", "alle former for opphold (whitespace)", etc. De kan også inverteres – dvs. å treffe alt annet enn det tegnklassen inneholder.
Tegnene du vil skal inkluderes i tegnklassen din putter du mellom [ og ] – for eksempel [aeioyuæøå] for å se etter vokaler. For bokstaver og tall kan du bruke "-" til å indikere et intervall – for eksempel [a-f0-9] for å matche gyldige tegn i et tall i heksadesimal.
Følgende tabell innebygde tegnklasser.
| Indikator | Funksjon |
|---|---|
| . | Et vilkårlig tegn. |
| [tegn] | Matcher alle tegn som forekommer mellom [ og ]. |
| [^tegn] | Matcher alle tegn som ikke forekommer mellom [ og ]. (Merk ^) |
| \d | Et tall |
| \D | Alt annet enn tall |
| \w | Et alfanumerisk tegn. Defineres av locale. |
| \W | Alt annet enn alfanumeriske tegn. Defineres av locale. |
| \s | Et opphold (mellomrom, linjeskift, tabulator, etc.) |
| \S | Alt annet enn opphold. |
\w og \W bruker locale til å finne ut hva som er gyldige bokstaver. Norske tegn som «æ», «ø» og «å» vil ikke matches av \w når man bruker engelsk locale, men vil det med norsk locale – på samme måte vil ikke «ß» være et gyldig tegn i norsk locale, men vil det i tysk locale.
Når du bruker [] og [^] for å lage egne tegnklasser, kan du bruke eksisterende tegnklasser som for eksempel \d. Eksemplet ovenfor kan da skrives om til [a-f\d].
| Tekst | Regulært uttrykk |
|---|---|
| 127.0.0.1 | (\d{1,3}\.){3}\d{1,3} |
| md5: d54f9200b680ff11eb1ffcb01a99bde2 | [a-f\d]{32} |
| Hei!! | \w* |
| Hei!! | \W* |
| SOS: ...---... | [\.\-]* |
| Indikator | Funksjon |
|---|---|
| \b | Ordgrense. Dvs. Et tegn i \w på den ene siden og \W på den andre. |
| \B | Alt annet enn ordgrenser. |
| \A | Begynnelsen av stringen. (Alà ^, men ^ opererer per linje.) |
| \Z | Slutten av stringen, eventuelt før siste linjeskift. (Ala $, men $ opererer per linje.) |
| \z | På slutten av stringen. |
| \G | Ved posisjonen til pos(). (Esoterisk)
|
Av disse er \b den mest nyttige. Hvis du for eksempel vil se etter forekomsten av ordet «en» i en tekst, vil det regulære uttrykket en komme til kort – det vil treffe alt for mye, som uthevingene viser. Med \ben\b vil du kun treffe forekomster som ikke har andre bokstaver ved siden av seg.
Spesialtegn
Store og små bokstaver
Grådige uttrykk
Alternativer
Tilbakereferanser – hente ut tekst
Erstatte tekst
Vanlige fallgruver
Eksempler
Regulære uttrykk beskrives best ved eksempler:
| Regulært uttrykk | Funksjon |
|---|---|
| ^\d+ | Matcher ett eller flere tall i begynnelsen av en streng |
| ^\s*$ | Matcher en linje som enten er helt tom eller bare inneholder mellomrom |
| \w{5,8} | Matcher alle forekomster av fem til åtte etterfølgende bokstaver |
| ^\d{4} | Matcher eksakt fire tall i begynnelsen av en streng |
| sykkel$ | Matcher alle linjer som har ordet "sykkel" helt på slutten |
| \$$ | Matcher alle linjer som har et dollartegn helt på slutten |
Denne leser ut alle linjer i /etc/passwd som inneholder teksten "root".
$ grep root /etc/passwd
root:x:0:0:root:/root:/bin/bash
Denne forsøker å lese ut alle linjer i /etc/hosts som IKKE inneholder ^$, dvs tom linje, eller ^#, dvs linje som begynner med # (kommentarlinje). Som vi ser virker det dårlig, fordi vi ikke benytter egrep eller bryteren -E.
$ grep -v "^$|^#" /etc/hosts
# Do not remove the following line, or various programs
# that require network functionality will fail.
127.0.0.1 localhost.localdomain localhost
De tre nedenstående eksemplene gjør det vi vil. Den første løsningen er noe mer tungvinn enn de to neste.
$ grep -v ^\# /etc/hosts | grep -v ^$
127.0.0.1 localhost.localdomain localhost
$ grep -Ev "^$|^#" /etc/hosts
127.0.0.1 localhost.localdomain localhost
$ egrep -v "^$|^#" /etc/hosts
127.0.0.1 localhost.localdomain localhost

