Avancerade reguljära uttryck

Rubriken för detta inlägg kan vara något missvisande. Detta är egentligen inte mer avancerade reguljära uttryck än det jag skrev om i inlägget om grunläggande reguljära uttryck. Däremot kanske dessa inte används lika ofta. Det som däremot gör att rubriken fungerar är att man kan skriva mycket mer avancerade reguljära uttryck när man använder det som finns nedan.

Girighet och lathet

Reguljära uttryck är normalt giriga. Det betyder att de försöker matcha så mycket som det bara går.

Vi kan exempelvis tänka oss följande sträng

<b>bold text</b>, och lite <b>mer bold text</b>

För att hitta texten som finns innanför b-taggarna kan vi skriva
<b>.*</b>
Detta kommer dock inte bli så bra, eftersom vi har två b-taggar så kommer den att matcha
<b>bold text, och lite <b>mer bold text</b>
Vilket förmodligen är mer än vi föräntar oss.

Därför kan vi tala om att uttrycket skall vara lat och bara matcha minsta möjliga för att mönstret skall vara uppfyllt. Det gör man genom att ange ett frågetecken.

Vi kan istället använda följa uttryck:
<b>.*?</b>
Nu kommer den att matcha de två taggarna separat.
<b>bold text, och lite <b>mer bold text</b>

Bakåtreferens

Om vi vill leta efter alla a- och btaggar så kan man tänka sig att vi kan skriva såhär
<(a|b)>.*</(a|b)>
Problemet med detta uttryck är att den då kommer att matcha även <a>test</b>, vilket vi kanske inte vill. Vi kan därför använda oss av bakåtreferens för att uttrycket skall komma ihåg vilket av valen i en grupp som uppfylldes. För att använda bakåtreferens så använder man \n där n är siffran i ordningen för din grupp. Exempelvis:

<(a|b)>.*</\1>

Ovanstående kommer alltså bara att matcha <b>test</b> och <a>test</a>

Namngivna grupper

Ibland kan det vara svårt att komma ihåg vilket nummer en viss grupp har och därför har man möjlighet att namnge grupper. Vårt bakåtreferensexempel skulle då kunna se ut såhär.
<(?P<aellerb>a|b)>.*</(?P=aellerb)>

Ordgränser

Ibland vill man matcha ord exakt som de är och då kan man använda \b för att ange en ordgräns. \b betyder att det inte får vara en bokstav för att mönstret skall matchas.

\btest\b matchar ordet test. Matchar inte testar. Matchar däremot test: eftersom kolon i detta fallet inte är en bokstav.

Motsatsen till \b är \B och innebär om man omsluter ett ord med \B att det man matchar måste ingå i ett ord. Exempelvis
\Bes\B matchar inte es, men däremot matchar den test eftersom frasen es är omringad av bokstäver.

Atomiska grupper

Atomiska grupper används ofta för att få bättre prestanda på reguljära uttryck. Som exempel kan vi ta följande uttryck:
(c|j|f)#
Om vi använder ovanstående uttryck på texten csharp så kommer den först att gå in i gruppen och välja det första alternativet c. Sedan går den vidare till nästa tecken. Då inser den att strängen inte innehåller ett # och då kommer den att gå tillbaka till gruppen för att testa nästa alternativ. Detta är givetvis helt onödigt eftersom vi vet att strängen inte innehåller ett # och därmed kan den inte matcha. Det man då kan göra är att använda sig av atomiska grupper, genom att ange ?> i gruppen så blir den atomisk.
Exempelvis:
(?>c|j|f)# Här kommer den endast att gå in i gruppen en gång och inse att strängen inte kan matchas och därför blir detta uttrycket mer effektivt än det första.

Notera dock att man inte alltid vill använda sig av atomiska grupper.
Som exempel kan vi ta
a(bc|b)c där den kommer att matcha abcc och abc, medan den atomiska gruppen a(?>bc|b)c endast kommer att matcha abcc eftersom den aldrig kommer att prova det andra alternativet i gruppen.

Titta framför

Titta framför eller lookahead fungerar som ett påstående som är sant eller falskt. Det tar inte upp något tecken.

Om vi tänker oss att vi vill hitta alla förekomster där tecknet j inte följs av ett a. Då kan man inte skriva j[^a] eftersom [^a] faktiskt talar om att det skall vara ett tecken och vi säger ju bara att det inte får följas av ett a. Därmed skulle ett j inte matchas.

Ett annat exempel kan vara att vi vill matcha filnamn med följande mönster
.*\..*
Det fungerar ju bra, men om vi nu vill matcha alla filnamn förutom de som har filändelsen .bat? Det skulle behövas ett ganka komplext reguljärt uttryck för att få fram det, och om vi sedan vill utöka till att det inte får vara .bat eller .exe så skulle det bli ännu mer komplext. Då är titta framför ett bra alternativ.
För att använda titta framför anger man ?= i gruppen för att det måste förekomma, och ?! för att det inte får förekomma.

För att matcha filnamn som inte slutar på .bat kan vi använda oss av
.*\.(?!bat).*

Ovanstående matchar exempelvis default.aspx, setup.exe och config.txt

För att matcha filnamn som bara slutar på .bat kan vi använda oss av
.*\.(?=bat).*

Ovanstående matchar exempelvis autoexec.bat

Ett annat exempel kan vara om man vill validera lösenord där man vill kräva viss form av komplexitet. Då kan man exempelvis skriva:

((?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{9,})

Ovanstående exempel använder sig av titta framför (lookahead) för att säkerställa att strängen innehåller minst en siffra, minst en gemen och minst en versal. Utöver det kan det vara vilka tecken som helst, dock minst 9 tecken.

About these ads

3 kommentarer

  1. [...] Om du vill läsa vidare om reguljära uttryck så läs gärna mitt inlägg om avancerade reguljära uttryck. [...]

  2. [...] på att kontrollera lösenordet både på klientsidan och serversidan. Läs gärna mitt inlägg om avancerade reguljära uttryck för att se exempel på hur det kan lösas. Kontrollera att lösenordet inte är samma som delar av [...]

  3. [...] vara tillåtna. När det är gjort kör jag GetSafeHtml på det resultatet. Jag använder mig av reguljära uttryck för att välja ut [...]

RSS feed for comments on this post

Kommentarer inaktiverade.

Följ

Få meddelanden om nya inlägg via e-post.

%d bloggers like this: