Välkommen till en introduktion i regular expressions, vilket ofta kallas regexps
eller bara RE. På svenska skulle det bli "reguljära uttryck", "standardiserade uttryck" eller
"vanliga uttryck", men de termerna känns inte så bra utan svengelskan är lättare att förstå ;)
Först förklaras vad regular expressions är och dess syntax. Sedan kommer några
exempel med varierande komplexitet, och slutligen några verktyg som använder
regular expressions.
Introduktion
Ett
regular expression är ett textmönster som används för att passa in i text-strängar.
Textmönstret består av en kombination av alfanumeriska tecken samt specialtecken som kallas
meta-tecken. Nära släkt är faktiskt de söktecken (?,*) (
wildcard expression) som ofta
används vid filhantering.
Regular expression används på tre olika sätt: vanlig textsökning, sök-och-ersätt, samt
vid delning. Det senare är i grunden detsamma som omvänd sökning (reverse match), dvs
allting som mönstret ej passade in på.
Tecken
Innehållet i ett mönster är, enligt ovan, en kombination av alfanumeriska tecken och
meta-tecken.
Ett alfanumeriskt tecken (
alphanumeric character) är antingen ett tecken från alfabetet eller en siffra:
abcdefghijklmnopqrstuvwxyz 1234567890 |
Följande tecken är meta-tecken (
metacharacter):
\ | ( ) [ ] { } ^ $ * + ? . < > |
I praktiken kommer dock alla tecken som inte är ett meta-tecken att fungera som ett
alfanumeriskt tecken. Dessa kallas ofta literala tecken (
literal characters).
omvändare
Ett mycket speciellt tecken är bakåtvänt snedstreck
\ (
backslash), som
gör om ett meta-tecken till ett literalt tecken, samt alfanumeriska tecken till ett slags
meta-tecken eller meta-sekvens.
punkt
Ett annat speciellt tecken som ofta missförstås är punkt
. (
punctuation mark, or dot).
Punkten kommer inte, som många tror, att passa in på en punkt i en text, utan det är istället
ett meta-tecken som motsvarar vilket tecken som helst. Om du använder punkten för att hitta
slutet på en mening eller en decimalpunkt i ett tal, kommer du att få fel. Som nämnt ovan skall
punkten istället användas som literalt tecken med hjälp av bakåtvänt snedstreck. (
Egentligen är punkten ett specialfall av den generella kvantifieraren, se nedan.)
Se på följande exempel på ett sökmönster:
kommer inte bara passa in på numret 1.23 i en text, utan även på följande:
Om sökmönstret enbart skall passa in på decimaltalet måste det ändras till:
Glöm det inte!
|
Kvantifierare
Två av de vanligaste metatecknen är:
De kallas kvantifierare (
quantifier) eftersom de passar in på flera instanser av ett
tecken. Kvantifieraren avser alltid tecknet som är angivet till vänster om den.
- Stjärntecknet * (star sign) passar in på noll eller fler instanser av ett tecken.
- Plustecknet + (plus sign) passar in på ett eller fler instanser av ett tecken.
Så om du vill hitta alla ord som börjar med j kanske du vill ange:
och till din överraskning får enorma mängder träffar, till och med i ord som inte innehåller
bokstaven j...
Anledningen är att tecknet * passar in på noll eller flera tecken, och det
var det som hände, det passade in på noll tecken!
Konsekvensen är att i regular expressions har man möjlighet att hitta det som kallas
den tomma strängen, vilket helt enkelt är en sträng med noll i storlek.
Tomma strängar finns i alla texter, till exempel i ordet:
finns det tre tomma strängar. Det finns en precis innan g, en mellan g och
å samt en efter å.
Det här verkar kanske konstigt, men det finns (givetvis) användning för detta i mer
komplexa sökmönster.
Så med denna läxa ändrar vi sökmönstret till:
och genast får vi enbart ord med j.
|
frågetecken
Nästa metatecken är:
Frågetecknet
? (
question mark) betyder att mönstret skall passa in på ett
tecken eller inte alls (noll eller ett).
Till exempel sökmönstret:
kommer att passa in på någon av dessa rader:
|
generell kvantifierare
Dessa tre metatecken är helt enkelt specialiserade metoder av den mer generella kvantifieraren:
där
n och
m betyder den minsta repektive den största storleken för kvantifieraren.
Till exempel:
betyder att mönstret skall passa in på en eller upp till och med fem tecken.
|
Man kan strunta i m för att inte begränsa storleken uppåt:
vilket kommer att passa in på ett eller flera tecken. Med andra ord, precis detsamma som
+ gör.
- * är detsamma som {0, }
- + är detsamma som {1, }
- ? är detsamma som {0,1}
- . är detsamma som {1,1}
Man kan även strunta i kommatecknet , (comma) för att passa in på ett exakt antal tecken.
Till exempel:
passar in på exakt fem tecken, varken mer eller mindre.
|
Påstående
Nästa typ av metatecken är påståenden (
assertions), som kommer att passa in om ett
visst påstående är sant.
Det första teckenparet för påståenden är:
som kommer att passa in på början av en rad respektive i slutet på en rad.
Observera att i vissa implementeringar av regular expressions är det
tillåtet att ändra betydelsen till att istället passa in på början av en text respektive
i slutet på en text.
Dessa påståenden passar alltid in på en tom sträng, eller med andra ord på en viss position.
Till exempel följande sökmönster:
skulle passa in på alla rader som börjar med ordet Jag.
|
Nästa teckenpar för påstående är:
som passar in i början på ett ord repektive i slutet på ett ord.
De är mycket användbara för att hitta ett exakt ord, till exempel:
passar in på alla följande ord:
jul julgran julklapp jultomte |
Efter nedanstående förändring i sökmönstret:
passar det enbart in på ordet jul i texten.
|
Det sista man bör säga är att alla literala tecken egentligen är påståenden. Den enda skillnaden
är att literala tecken har en storlek. Därför används termen påstående enbart för de som har
storleken noll.
Grupper (delmönster)
En sak som du kanske har lagt märke till vid genomgången av kvantifierare är att i exemplen
avsåg de endast tecknet till vänster om sig. Vi skall diskutera hur denna begränsade användning
kan utökas.
Kvantifierare kan även användas på meta-tecken. (Att använda dem på påståenden vore dumt
eftersom längden på dessa är noll, och använda dem på flera gör ingen skillnad.) Men
att använda dem på grupper och sekvenser (se nedan) är mycket användbart.
Man kan skapa grupper, eller delmönster (subexpressions) som de kan kallas, genom att
använda parentes-teckenparet :
där startparentes
( (
start parenthesis) påbörjar delmönstret och slutparentes
) (
end parenthesis) avslutar det. Det går bra att använda delmönster inuti
delmönster.
Man kan kombinera delmönster med kvantifierare och påstående på följande vis:
vilket passar in på följande rader:
|
Alternativ
Nästa typ av metatecken är alternativ (
alternation) som gör det möjligt att
passa ett av flera ord. Metatecknet för alternativ är stuprör (
pipe):
Ett enkelt exempel på alternativ:
vilket antigen passar in på Bill, Linus, Steve eller Larry.
Om man kombinerar alternativ med delmönster och kvantifierare kan man få:
vilket passar in på:
jul julgran julklapp jultomte |
Med andra ord kan man bygga ett mönster som kan passa, fastän inte allt passar in:
(Santa (Claus|Klaus))|(Tomten) |
Som du kan se passar antingen vänster eller höger delmönster in, och inte båda. Detta kan
ibland vara användbart vid komplexa sökningar.
|
Sekvenser
Till slut finns det sekvenser (
sequences), vilka definierar sekvenser av tecken som kan
passa. Användbart när du inte söker ett visst ord utan snarare någonting som liknar ett ord.
Teckenparet för sekvenser är:
Alla tecken som placeras mellan hakparenteserna tolkas som literala tecken, även metatecken.
De enda undantagen är bindestreck (dash) - vilket används för uttrycka
ange ett flertal tecken enkelt, samt taktecken ^ som används för att negera en sekvens.
Till viss del påminner sekvenser om alternativ; endast ett av de listade tecknen kommer att
passa. Till exempel:
passar in en gemen bokstav i det engelska alfabetet (a till z).
En annan vanlig sekvens är:
som passar in på både gemener och versaler i det engelska alfabetet, samt siffror.
|
Om man kombinerar alternativ med påstående och kvantifierare kan mer intelligenta sökningar göras:
passar in på alla ord. Till exempel på:
jul Linus regular expression |
men inte på:
|
Om man istället vill hitta allt som inte är ord kan man använda mönstret:
som passar in på alla teckensekvenser som varken innehåller bokstäver eller siffror.
|
Observera att i vissa implementeringar av regular expressions
finns det koder för vissa vanliga sekvenser:
\d = [0-9] (en siffra)
\D = [^0-9] (ingen siffra)
\w = [a-zA-Z0-9] (ett alfanumeriskt tecken)
\W = [^a-zA-Z0-9] (inget alfanumeriskt tecken)
\s = [ \t\n\r\f] (ett mellanrum)
\S = [^ \t\n\r\f] (inget mellanrum)
|
Exempel
För att förstå
regular expressions ännu bättre finns följande användbara mönster.
Undersök dem och försök förstå exakt vad de gör. Experimentera gärna:
Flyttal: passar in på enkla flyttal,
t.ex "1.2" eller "-0.5":
Hexadecimala tal: passar in på hex-siffror (i C/C++ stil):
t.ex "0xcafebabe":
Protokoll validering: passar in på web-baserade protokoll,
såsom "http://", "ftp://" eller "https://":
Epost validering: detta exempel kommer bara att passa in på korrekta epost-adresser,
t.ex "[email protected]":
[a-z0-9_-]+(\.[a-z0-9_-]+)*@[a-z0-9_-]+(\.[a-z0-9_-]+)+ |
Epost validering #2: passar in på epost-adresser med ett namn innan,
t.ex "Bo Ek <[email protected]>":
("?[a-zA-Z]+"?[ \t]*)+\<[a-z0-9_-]+(\.[a-z0-9_-]+)*@[a-z0-9_-]+(\.[a-z0-9_-]+)+\> |
C/C++ includes:
^#include[ \t]+[<"][^>"]+[">] |
C++ end of line comments:
C/C++ span line comments: har en svaghet, kan du hitta den?
Utilities
Konceptet med
regular expressions kommer från Unix-världen, där det är väl använt
i bland annat kommandoradssökaren
grep samt editorer som
sed,
ed,
vi och
(X)Emacs. Även i
gawk och
perl finns stöd. Speciellt
perl har en mycket avancerad implementering av
regular expressions.
Sammanfattning
\ | Omvändare | växlar betydelse på literala och meta-tecken |
* | Kvantifierare | passar in på 0 till flera tecken |
+ | passar in på 1 till flera tecken |
? | passar in på 0 eller 1 tecken |
. | passar in på 1 tecken |
{ } | {n,m} passar in på n till m tecken |
^ | Påstående | passar in på början på en rad |
$ | passar in på slutet på en rad |
< | passar in på början på ett ord |
> | passar in på slutet på ett ord |
< > | passar in på ett ord |
( ) | Grupper | passar in på teckenkombinationer |
| | Alternativ | passar in på ett av flera alternativ |
[ ] | Sekvenser | passar in på ett tecken i en kedja av tecken.
- anger sekvensen och ^ negerar sekvensen |