Emissionen Nr 3 2003


Reg[eE]xp?

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.

Användbara till vadå?

Jo, regexpar är väldigt användbara när man vill göra sökningar i textfiler.

Jaha, so what liksom, jag kan ju trycka Ctrl-F i Word, är det något särskilt det?

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:

egrep

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.

Perl

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.

Mer regexpsyntax

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 \.\+\\\?

Regexp i verkligheten

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


Emissionen är Konglig Elektrosektionens tidning vid KTH.

Valid

W3C html, W3C css, WAI aaa.