RegEx - Søk etter tekst

Bilde av Marten Newhall fra Unsplash

Bilde av Marten Newhall fra Unsplash

Hva er Regular Expressions, og hvordan funker det?
Sist oppdatert 1. august 2021

RegEx (Regular Expressions / Reglære Uttrykk) er en måte å lete etter innhold i tekst på. Det en veldig regelbunden, streng og nøyaktig måte å søke på.

I praksis fungere det slik at man lager en RegEx, som brukes mot en tekst for å finne nøyaktige treff på det vi leter etter. For eksempel er det godt egnet hvis vi leter etter en bildeserie som følger ett spesielt filnavn mønster. Hvis vi drifter et webforum og ønsker å stoppe et forum innlegg som inneholder hatefulle ord og uttrykk fra å bli publisert, kan man bruke en RegEx på innlegget for å teste om det er "trygt" før det publiseres. En annen måte er å bruke RegEx på er å trigge spesielle hendelser på nettsider. Ett eksempel kan være at noen søker på spesielle søkeord.

RegEx kan brukes av de fleste programmering og script språk og er godt egnet som en måte å filtrere ut tekst innhold vi er interessert i, for å så gjøre noe automatisk med den teksten.

Et av Linux verktøyene som gjør at vi kan bruke RegEx i terminalen eller i bash script er programmet "grep" (Globally search for a Regular Expression and Print).

Grep er laget for å søke etter linjer i filer, eller kan bli brukt etter en "pipe" med hvor den søker igjennom STDOUT fra ett annet program. Grep vil matcher den RegEx'en du oppgir og printer det den finner til terminalen. Grep er et veldig effektivt og populært verktøy for å raskt finne og bare vise linjer med tekst som interesserer oss. I denne artikkelen har jeg ikke som mål å gi innføring i grep, men vi vil bruke det for å bli kjent med RegEx.

Det er i hovedsak to varianter av Regular Expressions som brukes. Det er BRE (Basic Regular Expressions) og ERE (Extended Regular Expressions). Jeg synes ERE er veldig mye lettere å lese og å lære, derfor vil jeg kun bruke det i denne artikkelen. Opprinnelig var det mer funksjonalitet i ERE enn det var i BRE, men etter hvert som tiden har gått er det i dag hovedsakelig forskjellig syntax som skiller de to variantene.

Noen programmer og programmerings språk støtter kun den en av de to variantene og andre støttet begge variantene. Med grep må vi spesifisere optionen eller flagget "-E" for å bruke ERE. Uten at det flagget brukes forventer grep at det brukes BRE.

1-1 match

Grunnleggende tekst søk

Hvis man bruker teksten under som eksempel (hentet fra snl.no) og lagrer dette i tekstfilen greptest.txt

greptest.txt
Internetts historie begynte i 1960-årene med en rekke forsøk på å utvikle standarder, såkalte protokoller, for sammenkobling av datamaskiner over lengre avstander. Tidligere former for oppringt samband gav svært dårlig utnyttelse av linjekapasiteten. Hver sammenkobling la beslag på en telefonlinje – det blir som å ha en vei der bare én bil har lov til å kjøre om gangen.
Dette er andre linje som vi håper at ikke blir printet med GREP :)

Hvis vi nå bruker ren tekst vil grep printe alle linjer som finner en 100% match med det vi brukte i uttrykket.

Hvis vi kun skal søke med ren tekst, uten å bruke RegEx funskjoner trenger vi ikke å bruke "-E" flagget med grep.

grep linje greptest.txt

Internetts historie begynte i 1960-årene med en rekke forsøk på å utvikle standarder, såkalte protokoller, for sammenkobling av datamaskiner over lengre avstander. Tidligere former for oppringt samband gav svært dårlig utnyttelse av linjekapasiteten. Hver sammenkobling la beslag på en telefonlinje – det blir som å ha en vei der bare én bil har lov til å kjøre om gangen.

Dette er en super måte å lete etter linjer som matcher det vi leter etter, og ønsker å være fleksibel. ett annet eksempel er å lete etter services i Linux som kjører, men vi husker ikke helt navnet på. Hva het egentlig brannmur deamonen til maskinen? Jeg kjører kommandoen systemctl for å liste alle daemonene som er installert på maskinen, og piper outputen fra det programmet til inputen til programmet grep, som vil forsøke å matche linjer som inneholder teksten "fw" (firewall)

sudo systemctl | grep fw

ufw.service

Flott! Da vet jeg at jeg kan skru av brannmuren med å kjøre kommandoen:

sudo systemctl stop ufw

^ $

Starten eller slutten av linjen

Spesial tegnene ^ og $ definerer starten og slutten av en linje. Derfor må vi dele opp teksten vi skal søke i slik at deg går over flere linjer. Vi lager en ny fil som vil døper greptest2.txt

greptest2.txt
Internetts historie begynte i 1960-årene med en rekke forsøk på å utvikle standarder,
såkalte protokoller, for sammenkobling av datamaskiner over lengre avstander
Tidligere former for oppringt samband gav svært dårlig utnyttelse av linjekapasiteten.
Hver sammenkobling la beslag på en telefonlinje – det blir som å ha en vei der
bare én bil har lov til å kjøre om gangen.

^ er ett spesielt tegn som brukes i RegEx til å treffe starten av en linje. Ved å bruke ^ etterfulgt av en søkestreng leter vi etter en linje som starter med søke strengen.

grep -E "^Intern" greptest2.txt

Internetts historie begynte i 1960-årene med en rekke forsøk på å utvikle standarder,

Her fant vi bare en linje. siden det kun er en eneste linje som starter med "Intern".

$ er ett spesielt tegn som brukes til å treffe slutten av en linje. Ved å bruke $ i slutten av søkestrengen leter vi etter en linje som slutter med søkestrengen at man søker etter linjer som slutter på søke strengen vår.

grep -E "avstander$" greptest2.txt

såkalte protokoller, for sammenkobling av datamaskiner over lengre avstander

Her fant vi også bare en linje som traff vårt søk. Hvis vi ser etter, ligger denne linjen i den første paragrafen.

? + *

Repetisjoner av tegnet foran

Spesialtegnene ? + og * kan brukes til å lete etter antall gjentagelser av noe vi søker etter.

For å illustrere bruken av disse spesial tegnene lager vi en ny fil som vi døper greptest3.txt:

greptest3.txt
1
10
100
1000

? er ett spesielt tegn som brukes i RegEx til å lete etter ingen eller en gjentagelser av bokstaven, tallet eller tegnet foran.

grep -E "10?" greptest3.txt

1
10
10
0
10
00

+ er ett spesielt tegn som brukes i RegEx til å lete etter en eller flere gjentagelser av bokstaven, tallet eller tegnet foran +.

grep -E "10+" greptest3.txt

10
100
1000

Her søker vi ut tallet 1, og en eller uendelig mange nuller. Søket vil dermed ikke finne den første linjen i filen vår.

* er ett spesielt tegn som bukes i RegEx til å leter etter ingen eller flere gjentagelser av bokstaven, tallet eller tegnet forran. Sagt på en annen måte er den valgfri.

grep -E "10*" greptest3.txt

1
10
100
1000

Siden tallet null var valgfri og kunne poetnsiellt bli gjentatt endelig mange ganger finner søket alle linjene i filen.

.

bokstav, tall eller tegn

. er ett spesielt tegn i RegEx som representerer ett vilkårlig tegn. Men bare ett tegn. Dette kan være en tore og små bosktaver, tall eller symbol. Med RegEx utrykket "1.3" kan finne matcher som blant annet:

123
103
1a3
1F3
1!3

brukt i grep med filen greptest2.txt som vi laget tidligere, finner følgende linjer:

grep -E "p." greptest2.txt

Internetts historie begynte i 1960-årene med en rekke forsøk å utvikle standarder,
såkalte protokoller, for sammenkobling av datamaskiner over lengre avstander.
Tidligere former for oppringt samband gav svært dårlig utnyttelse av linjekapasiteten.
Hver sammenkobling la beslag en telefonlinje – det blir som å ha en vei der

\d \D

Tall og ikke-tall

\d brukes i Regex til å søke etter et hvilket som helst tall mellom 0 og 9.

\D brukes i RegEx til å søke etter bokstaver eller tegn.

Disse to kan for eksempel brukes der man vil være mere nøyaktig enn å bruke . i søket, og har bestemt seg for å bare søke søke etter tall. RegEx uttrykket "Russ20\d\d" kan vi for eksempel finne:

Russ2006
Russ2020

[ ]

Rekke med tall, bokstaver eller tegn

[ ] brukes i RegEx rundt en rekke med med tall, bokstaver eller tegn vi ønsker å søke mot. Så istedet for å godta hvilket som helst tall, bokstav eller tegn med . bruker vi [ ] for å begrense hva vi vil godta. Et eksempel er RegEx utryket

ju[nl]i

Som kan finne:

juni
juli

-

Fra og til

Hvis vi bruker [ ] og rekken med tall og bokstaver vi ønsker å godta for eksempel er mellom a til f, eller 3-8 kan vi bruke [a-f] eller [3-8], for å slippe å skrive [abcdef] eller [345678].

Det er viktig å tenke over det er forskjell på store og små bokstaver. [a-f] er ikke det samme som [A-F].

Hvis jeg skal søke etter alle sko i en varebeholdning som er i skostørrelse 40 til 45 til menn i en varenummer liste som ser slik ut:

2020AWM40 <-- 2020 modell, Allround, Winter, Male, størrelse 40
2020AWF38 <-- 2020 modell, Allround, Winter, Female, størrelse 38

kan jeg skrive følgende RegEx:

2020AWM4[0-5]

Dette kan gi meg følgende varenummer:

2020AWM40
2020AWM41
2020AWM42
2020AWM43
2020AWM44
2020AWM45

|

Eller

| brukes i RegEx til skille mellom to alternativer på denne måten:

[a-d]|[o-r]

Dette vil gi følgende treff:

a
b
c
d
o
p
q
r

( )

Gruppering

Akkurat som i matematikken kan man i RegEx bruke ( ) til å gruppere deler av uttrykket sammen. Dette er spesielt nyttig i kombinasjon med | for å se på to alternativer av ett uttrykk. vi kan for eksempel skrive:

(Dame|Herre)klær

Dette vil gi følgende treff:

Dameklær
Herreklær

\

Escape

Siden RegEx bruker tegn som kan være brukt i teksten vi ønsker å søke i bruker vi \ for å si til RegEx at bokstaven som kommer etter skal ikke tolkes som ett RegEx symbol, men som en et vanlig symbol i teksten. På fagspråket heter dette en "Escape character". Dette er praktisk hvis vi for eksempel skal bruke RegEx i en URL's siden de blant annet inneholder . og ?.

Hvis vi ønsker å lete etter den tenkte URL'en "www.vg.no?articleId=22" må vi skrive RegEx utrykket:

www\.vg\.no\?articleId=22