Du som har använt Unix ett tag har säkert någon gång hört
talas om så kallade regexpar
och undrat vad det egentligen
är för något.
Vi ska nu fösa ut dessa sägenomspunna varelser ur
Unix-djungelns mörkaste skumrask.
Hej alla barn, nu blir det datorprogram! Vi
tänkte tala lite om regular expressions. De som talar
ärans och hjältarnas språk säger hellre reguljära
uttryck
. Vi tycker emellertid att det är för jobbigt, så vi
kallar dem bara för regexpar
(regexp-ar
, inte
regex-par
). Vad är då ett regexp?
Det finns en del myter om regexpar som vi borde ta upp på en gång. En av dessa är att regexpar skulle vara svåra att förstå sig på. Det är sant. En annan är att det finns olika varianter som är inkompatibla med varandra. Det är också sant. Men om man bortser från det (och det gör man) så är regexpar kollossalt kraftfulla och alldeles oerhört användbara.
Jo, regexpar är väldigt användbara när man vill göra sökningar i textfiler.
Ja, men då kan du bara söka efter fasta textsträngar. Med hjälp av regexpar kan du göra mönster som kan matcha en mängd olika stycken text. Vi kanske ska ta ett exempel:
Ett program som du kanske har kommit i kontakt med är
grep (vi kommer här att använda den förbättrade
varianten egrep). Du kan till exempel skriva
egrep 'zebra' safari.txt för att finna alla rader i filen
safari.txt som innehåller ordet zebra
. Vad
inte alla känner till är att grep tolkar uttrycket zebra
som ett regexp, men så länge du bara använder bokstäver i
uttrycket så märker du ingenting. Men vänta nu, tänk om
författaren till safari.txt inte har varit konsekvent, utan ibland
skrivit sebra
? Det är här regexpar kommer till
användning. Om du skriver egrep '[sz]ebra' safari.txt så
kommer egrep att hitta rader som innehåller både zebra
och
sebra
. Det är nämligen så att uttrycket [sz]
betyder matcha antingen s eller z, men ingenting annat.
Problemet är nu bara att |sz]ebra inte bara matchar
hela ord utan även delar av ord. Om safari.txt även innehåller
ordet resebransch
, så kommer även det att hittas. Om man
bara vill hitta hela ord kan man skriva \b|sz]ebra\b, där
\b betyder matcha början eller slutet på ett ord.
Nu finns det ju andra program än egrep. Ett sådant är
Perl. Perl är, som du kanske känner till, ett
mycket kraftfullt skriptspråk. Ta det lugnt, vi ska inte inte
skriva hela program – vi ska bara använda ett enda
Perl-kommando. Ponera att Sveriges Riksdag har bestämt att alla
som heter Svensson i efternamn ska byta namn till af
Silfverfiisk
. Då måste du ju göra ändringar i din stora
illegala persondatabas, så du skriver
perl -pi -e 's/Svensson/af Silfverfiisk/g' databas.txt. Detta
applicerar Perl-kommandot s/foo/bar/g på databas.txt, där
all text som matchar regexpet foo ersätts med texten
bar. För att även få med alla som stavar med ett s, så
kan du ändra Svensson till Svenss?on eftersom
tecknet ? betyder matcha föregående tecken en gång
eller inte alls.
Om du vill få med familjen Svenzon också så
kan du skriva Sven(ss?|z)on, där (foo|bar)
betyder matcha antingen foo eller bar.
Om nu den den feministiska revolutionen visar sig vara ett
faktum och alla som har ett efternamn som slutar på -son
måste ändra det till -dotter
, så kan kanske följande
kommando vara användbart: perl -pi -e 's/\b(\w+)son/\1dotter/g'
databas.txt. Det ser kanske lite komplicerat ut, men vi tar
det steg för steg. Som tidigare matchar \b början på ett
ord, medan \w betyder matcha en bokstav eller en
siffra
. Tecknet + betyder matcha föregående tecken
en eller flera gånger.
Parenteserna används för att dela av en
del av regexpet och har ingen betydelse för själva
matchningen. Uttrycket matchar därför alla ord som slutar på
son
.
I ersättningstexten har vi använt \1 som betyder
infoga texten som matchades av uttrycket inom den första
parentesen i regexpet,
i det här fallet allting innan
son
. Karlsson blir Karlsdotter, Johansson blir
Johansdotter, etc.
Tecknet ^ matchar början på en rad, medan $ matchar slutet på en rad. Man kan t.ex. skriva ^A för att matcha alla rader som börjar med A eller A$ för att matcha alla rader som slutar med A.
Som vi redan har sett kan man skriva t.ex. [abcdef] för att matcha a, b, c, d, e, eller f. Detta kan man förkorta till [a-f]. Om man använder tecknet ^ som [^a-f] så matchas vilket tecken som helst utom a, b, c, d, e, och f. (Detta har inget med början på raden att göra.)
Tecknet . matchar vilket tecken som helst, inklusive mellanslag. Därför matchar \bo.*s\b alla ord som börjar på o och slutar på s. (* fungerar likadant som +, med det undantaget att den matchar noll eller flera gånger.) Eftersom . matchar även mellanslag så hittas även listor på ord, där det första börjar på o och det sista slutar på s. Det kan man lösa genom att skriva \bo\w*s\b
Om man vill matcha tecken som har en speciell betydelse i
regexpar så måste man skriva ett bakåtstreck (\)
innan. För att matcha texten .+\?
måste man skriva
\.\+\\\?
Exemplen i den här introduktionen kanske verkar lite krystade, men det är inte svårt att finna verkliga användningsområden för regexpar. Tänk dig att du håller på med ett stort programmeringsprojekt som består av ett stort antal källkodsfiler. Om du då bestämmer att du vill byta namn på en variabel i källkoden så måste du förmodligen ändra i flera filer. Då är det mycket enklare att använda t.ex. find tillsammans med Perl istället för att öppna varje fil i Emacs och göra sök/ersätt. (Vi kanske tar upp det i en senare artikel.)
Men vi ska inte tala illa om Emacs, som naturligtvis också har stöd för regexpar. Alla sök/ersätt-funktioner har varianter där man kan använda regexpar. Mycket av det som sker bakom kulisserna när det gäller syntaxfärger och indentering är också baserat på regexpar.
Bibliotek för att hantera regexpar finns till de flesta programmeringsspråk. Vi valde Perl som exempel, dels därför att regexpar är integrerat i språket och dels för att Perl i mångt och mycket har kommit att ersätta klassiska Unix-verktyg som awk och sed.
Stöd för regexpar finns också i en mängd andra program och även i en del mindre sökmotorer (inte Google). Om du vill lära dig mer om regexpar finns det massor av information på nätet. Du kan också läsa info-dokumentationen med info regex eller Perls man-blad man perlre eller man perlretut (gör module add perl först!)
Fredrik Arnerup