762 lines
22 KiB
TeX
762 lines
22 KiB
TeX
\documentclass[screen]{beamer}
|
|
|
|
\usepackage[T1]{fontenc}
|
|
\usepackage[latin1]{inputenc}
|
|
|
|
\usepackage{beamerthemesplit}
|
|
|
|
\parindent=0pt
|
|
\parskip=0.5\baselineskip
|
|
|
|
\title{Regex i Perl}
|
|
\author{Knut Auvor Grythe}
|
|
\date{\today}
|
|
|
|
\begin{document}
|
|
|
|
\frame{\titlepage}
|
|
|
|
%\section[Innhold]{}
|
|
%\frame{\tableofcontents}
|
|
|
|
\section{Introduksjon}
|
|
\subsection{Litt om regex}
|
|
\frame
|
|
{
|
|
\frametitle{Hva er Regex?}
|
|
|
|
\begin{itemize}
|
|
\item Forkortelse for ``Regular Expression'' (``regulært uttrykk'' på norsk).
|
|
\item En kjapp og grei måte å gjenkjenne enkle tekst-strukturer på.
|
|
\item Velegnet for å gjenkjenne eller modifisere tekst på et gitt format.
|
|
\item Veldig kompakt syntaks, og dermed lite å skrive.
|
|
\item Kan brukes i de fleste programmeringsspråk (men har ofte litt varierende syntaks)
|
|
\end{itemize}
|
|
}
|
|
|
|
\frame
|
|
{
|
|
\frametitle{Hvordan ser en regex ut?}
|
|
|
|
\begin{itemize}
|
|
\item En regex er i utgangspunktet bare en tekststreng, der noen få av tegnene har en spesiell betydning.
|
|
\item I en del språk (for eksempel Perl) skrives regexer med en skråstrek før og etter.
|
|
\item Denne regexen gjenkjenner teksten ``banan'': /banan/
|
|
\end{itemize}
|
|
}
|
|
|
|
\frame
|
|
{
|
|
\frametitle{Hva gjør den egentlig?}
|
|
|
|
\begin{itemize}
|
|
\item En regex går gjennom teksten \textit{fra venstre til høyre}, helt til den finner et treff.
|
|
\item En regex er grådig. Om to ulike treff begynner på samme sted i teksten, vil den alltid ta det lengste.
|
|
\item Hva som skjer når regexen matcher noe avhenger av hvordan den er brukt.
|
|
\end{itemize}
|
|
}
|
|
|
|
\subsection{Praktisk bruk}
|
|
\frame
|
|
{
|
|
\frametitle{Bruksområder i Perl}
|
|
|
|
Tre vanlige bruksområder for regex i perl er matching, substitusjon og
|
|
splitting. Det finnes også andre bruksområder, men da er det i praksis
|
|
matching som utføres.
|
|
|
|
\begin{block}{Matching}
|
|
if (\$var =\~{} /banan/) \{
|
|
do\_stuff();
|
|
\}
|
|
\end{block}
|
|
|
|
\begin{block}{Substitusjon}
|
|
\$var =\~{} s/banan/eple/;
|
|
\end{block}
|
|
|
|
\begin{block}{Splitting}
|
|
my @array = split(/a/, ``banan'');
|
|
\end{block}
|
|
}
|
|
|
|
\frame
|
|
{
|
|
\frametitle{Hvordan bruke andre tegn enn //}
|
|
|
|
Selv om det er standard å skrive regexen inni //, er det fullt lovlig å
|
|
bruke andre tegn i stedet. Dette er kjekt om du skal matche et pattern med
|
|
mange /-er i, og ikke har lyst til å escape alle sammen.
|
|
|
|
For at Perl skal kjenne igjen matching må man skrive en ``m'' foran
|
|
uttrykket, dersom man bruker noe annet enn //.
|
|
|
|
Det er ganske vanlig å bruke komma eller kolon som tegn.
|
|
|
|
\begin{block}{Matching}
|
|
if (\$var =\~{} m,banan,) \{
|
|
do\_stuff();
|
|
\}
|
|
\end{block}
|
|
|
|
\begin{block}{Substitusjon}
|
|
\$var =\~{} s,banan,eple,;
|
|
\end{block}
|
|
|
|
}
|
|
|
|
\frame
|
|
{
|
|
\frametitle{Modifikatorer}
|
|
Modifikatorer er ting du kan skrive bak uttrykket ditt for å få det til å
|
|
oppføre seg litt annerledes. Vi konsentrerer oss om de to viktigste, /g og /i.
|
|
|
|
Modifikatorer brukes ved å plassere dem rett bak siste / i utrykket.
|
|
|
|
\begin{block}{Eksempler}
|
|
\begin{tabular}{ll}
|
|
\textbf{/abcd/i} & et matche-uttrykk med /i.\\
|
|
\textbf{s/abcd/blarb/g} & et substitusjons-uttrykk med /g.\\
|
|
\textbf{s/abcd/blarb/ig} & et substitusjons-uttrykk med både /g og /i.\\
|
|
\end{tabular}
|
|
\end{block}
|
|
}
|
|
|
|
\frame
|
|
{
|
|
\frametitle{Modifikatorer - Hva de gjør}
|
|
|
|
Nå som vi vet hvordan vi slenger på en modifikator kan vi kanskje få vite
|
|
hva de gjør også :-)
|
|
|
|
\textbf{/i} gjør at uttrykket ikke skiller på store og små bokstaver
|
|
(case-insensitive). Litt tregere enn å bare skrive inn store og små
|
|
bokstaver manuelt, siden den følger gjeldende locale, men neppe noe problem
|
|
i praksis.
|
|
|
|
\textbf{/g} brukes kun i substitusjon, og står for ``global''. Vanligvis
|
|
vil substitusjonen bare bytte ut første treff, men med denne vil den bytte
|
|
ut \textit{alle} treff.
|
|
}
|
|
|
|
\frame
|
|
{
|
|
\frametitle{Kommandolinje-testing av matche-uttrykk (``grepping'')}
|
|
|
|
\begin{block}{Perl}
|
|
echo "vi liker bananer" | perl -ne 'print if /banan/'
|
|
\end{block}
|
|
|
|
\begin{block}{Andre språk}
|
|
echo "vi liker bananer" | grep 'banan'\newline
|
|
echo "vi liker bananer" | sed -e '/banan/p' -e 'd'\newline
|
|
echo "vi liker bananer" | awk '/banan/ \{print\}'
|
|
\end{block}
|
|
|
|
}
|
|
|
|
\frame
|
|
{
|
|
\frametitle{Kommandolinje-testing av substitusjons-uttrykk (``sedding'')}
|
|
|
|
\begin{block}{Perl}
|
|
echo "vi liker bananer" | perl -pe 's/banan/rosin/'
|
|
\end{block}
|
|
|
|
\begin{block}{Andre språk}
|
|
echo "vi liker bananer" | sed 's/banan/rosin/'\newline
|
|
echo "vi liker bananer" | awk '\{sub(/banan/, "rosin"); print\}'
|
|
\end{block}
|
|
|
|
}
|
|
|
|
\section{Oversikt over syntaks}
|
|
\subsection{Spesielle tegn og hva de gjør}
|
|
|
|
\frame
|
|
{
|
|
\frametitle{Å matche en helt vanlig tekst-streng}
|
|
|
|
Om du skal matche en helt vanlig tekst-streng, kan du bare skrive den rett
|
|
ut, som for eksempel /banan/ som vi har sett før.
|
|
|
|
\begin{block}{NB!}
|
|
Husk imidlertid at enkelte tegn har spesiell betydning, og disse må escapes
|
|
om du vil ha selve tegnet. Et eksempel på dette er punktum, som kan matches
|
|
med /$\backslash$./.
|
|
|
|
Den største forskjellen på de ulike regex-dialektene er hva som skal
|
|
escapes. En del språk tolker alt som tekst, og bruker spesialbetydningen
|
|
når du escaper i stedet. Hva andre språk gjør er imidlertid litt utenfor
|
|
skopet til kurset.
|
|
\end{block}
|
|
}
|
|
|
|
\frame
|
|
{
|
|
\frametitle{Liste over magiske tegn(-sekvenser)}
|
|
|
|
\begin{tabular}{ll}
|
|
$\backslash$ & Quote the next metacharacter\\
|
|
. & Match any character (except newline)\\
|
|
* & Match 0 or more times\\
|
|
+ & Match 1 or more times\\
|
|
? & Match 1 or 0 times\\
|
|
\{n\} & Match exactly n times\\
|
|
\{n,\} & Match at least n times\\
|
|
\{n,m\} & Match at least n but not more than m times\\
|
|
\^{} & Match the beginning of the line\\
|
|
\$ & Match the end of the line (or before newline at the end)\\
|
|
| & Alternation\\
|
|
() & Grouping\\
|
|
$[ ]$ & Character class\\
|
|
\end{tabular}
|
|
}
|
|
|
|
\frame
|
|
{
|
|
\frametitle{Tegnet . (punktum)}
|
|
|
|
. (punktum) matcher et vilkårlig tegn.
|
|
|
|
\begin{block}{Eksempel}
|
|
\textbf{/bl.pp/} matcher ``blipp'', ``blapp'', ``blupp'' og så videre.
|
|
\end{block}
|
|
|
|
\begin{block}{NB:}
|
|
``Vilkårlige tegn'' innebærer også whitespace, men per default ikke linjeskift (kan endres med en modifier vi snakker om senere).
|
|
\end{block}
|
|
}
|
|
|
|
\frame
|
|
{
|
|
\frametitle{Tegnet * (stjerne)}
|
|
|
|
* (stjerne) sier at forrige tegn skal matches 0 eller flere ganger.
|
|
|
|
\begin{block}{Eksempel 1}
|
|
\textbf{/bla*pp/} matcher ``blpp'', ``blapp'', ``blaapp'', ``blaaapp'' og så videre.
|
|
\end{block}
|
|
|
|
\begin{block}{Eksempel 2}
|
|
\textbf{/bl.*pp/} matcher alt i Eksempel 1, samt f.eks. ``blupp'', ``blåbærkopp'', ``blå muggsopp'' og så videre.
|
|
\end{block}
|
|
}
|
|
|
|
\frame
|
|
{
|
|
\frametitle{Tegnet + (pluss)}
|
|
|
|
+ (pluss) fungerer akkurat som * (stjerne), bortsett fra at det er ``1 eller flere'' i stedet for ``0 eller flere''.
|
|
|
|
\begin{block}{Eksempel}
|
|
\textbf{/bla+pp/} matcher ``blapp'', ``blaapp'', ``blaaapp'' og så videre, men ikke ``blpp'' (som \textbf{/bla*pp/} ville matchet).
|
|
\end{block}
|
|
}
|
|
|
|
\frame
|
|
{
|
|
\frametitle{Tegnet ? (spørsmålstegn)}
|
|
|
|
? (spørsmålstegn) sier at forrige tegn skal matches ``0 eller 1'' ganger.
|
|
|
|
\begin{block}{Eksempel}
|
|
\textbf{/fisk?/} matcher ``fisk'' og ``fis'', og ingenting annet.
|
|
\end{block}
|
|
}
|
|
|
|
\frame
|
|
{
|
|
\frametitle{Tegnene \{ og \} (krøllparenteser)}
|
|
|
|
\{ og \} brukes for å gi et egendefinert intervall over hvor mange ganger forrige tegn skal matches.
|
|
|
|
\begin{block}{Eksempler}
|
|
\textbf{/fi\{3\}sk/} matcher ``fiiisk''.\newline
|
|
\textbf{/fi\{2,3\}sk/} matcher ``fiisk'' og ``fiiisk''.\newline
|
|
\textbf{/fi\{2,\}sk/} matcher ``fiisk'', ``fiiisk'', ``fiiiisk'' og så videre.
|
|
\end{block}
|
|
|
|
\begin{block}{NB!}
|
|
Første argument er ikke valgfritt! \textbf{/fi\{,3\}sk/} matcher faktisk bare ``fi\{,3\}sk''.
|
|
\end{block}
|
|
}
|
|
|
|
\frame
|
|
{
|
|
\frametitle{Sammenhengen mellom kvantifikatorene)}
|
|
|
|
\begin{tabular}{|l|l|}\hline
|
|
\textbf{Tegn} & \textbf{Krøllparentes-uttrykk} \\ \hline
|
|
* & \{0,\} \\
|
|
+ & \{1,\} \\
|
|
? & \{0,1\} \\ \hline
|
|
\end{tabular}
|
|
|
|
Du kan med andre ord bruke kun \{ og \} om du vil, men ingen gjør det i praksis.
|
|
}
|
|
|
|
\frame
|
|
{
|
|
\frametitle{Tegnet \^{} og \$ (hatt og dollar)}
|
|
|
|
\^{} og \$ matcher henholdsvis ``start of line'' og ``end of line''.
|
|
|
|
Det vil altså si endene på variablene. Dersom det siste tegnet i variabelen
|
|
er et linjeskift, matcher den også rett før linjeskiftet.
|
|
|
|
\begin{block}{Eksempler}
|
|
\textbf{/\^{}banan/} matcher ``banankompott'', men ikke ``stor banan''.\newline
|
|
\textbf{/banan\$/} matcher ``stor banan'', men ikke ``banankompott''.\newline
|
|
\textbf{/\^{}banan\$/} matcher kun ``banan'' eller ``banan$\backslash$n''.
|
|
\end{block}
|
|
}
|
|
|
|
\frame
|
|
{
|
|
\frametitle{Tegnet | (pipe)}
|
|
|
|
| (pipe) betyr ``eller'', og er sikkert velkjent fra boolsk algebra.
|
|
Det kan settes mellom to eller flere alternativer for match.
|
|
|
|
\begin{block}{Eksempel 1}
|
|
\textbf{/banan|eple/} matcher både ``banan'' og ``eple'' (og strenger som inneholder en eller flere av dem av dem).
|
|
\end{block}
|
|
|
|
\begin{block}{Eksempel 2}
|
|
\textbf{/banan|eple|pære/} matcher det samme som Eksempel 1, pluss ``pære''.
|
|
\end{block}
|
|
}
|
|
|
|
\frame
|
|
{
|
|
\frametitle{Tegnene ``('' og ``)'' (parenteser)}
|
|
|
|
Parenteser brukes til å gruppere, mye på samme måten som vi er vant til fra
|
|
boolsk algebra.
|
|
|
|
De brukes også til litt annet småtteri, som vi skal se på snart.
|
|
|
|
\begin{block}{Eksempel}
|
|
\textbf{/\^{}(banan|sviske)kompott\$/} matcher ``banankompott'' og ``sviskekompott''.
|
|
\end{block}
|
|
}
|
|
|
|
\frame
|
|
{
|
|
\frametitle{Tegnene [ og ] (firkantparenteser)}
|
|
|
|
Firkantparenteser brukes når det er flere alternativer til et tegn.
|
|
Tegnene skrives rett og slett etter hverandre inni firkantparentesene.
|
|
|
|
Firkantparenteser er egentlig et kapittel for seg, og kommer mer utfyllende senere.
|
|
|
|
\begin{block}{Eksempel}
|
|
\textbf{/bl$[$aui$]$pp/} matcher ``blapp'', ``blupp'' eller ``blipp''.
|
|
\end{block}
|
|
}
|
|
|
|
\subsection{Mer om gruppering}
|
|
|
|
\frame
|
|
{
|
|
\frametitle{Mer om parenteser}
|
|
|
|
Som vi så for ikke så lenge siden, brukes parenteser til å gruppere ting
|
|
med. Vi så eksempelet \textbf{/\^{}(banan|sviske)kompott\$/}, der
|
|
parenteser brukes til å avgrense skopet til variable.
|
|
|
|
Parenteser kan imidlertid også brukes til å hente ut en substreng av det
|
|
som ble matchet, og det kan vi bruke til mye rart.
|
|
}
|
|
|
|
\frame
|
|
{
|
|
\frametitle{Variablene \$1, \$2, \$3, etc. (1)}
|
|
|
|
Når et uttrykk med parenteser i matcher, blir innholdet i parentesene lagt
|
|
i spesielle variable, kalt ``tilbakereferanser''. Den første påbegynte
|
|
parentesen blir lagt i \$1, den neste i \$2, og så videre.
|
|
|
|
Merk at disse variablene må brukes med en gang, siden de forsvinner så fort
|
|
du gjør en ny regex-matching lenger nede i koden. Ta en kopi!
|
|
}
|
|
|
|
\frame
|
|
{
|
|
\frametitle{Variablene \$1, \$2, \$3, etc. (2)}
|
|
|
|
Dette eksempelet plasserer ``en'' i \$a og ``to'' i \$b dersom uttrykket
|
|
matcher (og det gjør det).
|
|
|
|
\begin{block}{Eksempel}
|
|
my \$var = "en to";\newline
|
|
my \$a;\newline
|
|
my \$b;
|
|
|
|
if (\$var =\~{} /(.*) (.*)/) \{\newline
|
|
\hspace*{1em} \$a = \$1;\newline
|
|
\hspace*{1em} \$b = \$2;\newline
|
|
\}
|
|
\end{block}
|
|
|
|
}
|
|
|
|
\frame
|
|
{
|
|
\frametitle{Direkte uthenting av tilbakereferanser}
|
|
|
|
Om du kaller en matching i array-kontekst returneres alle
|
|
tilbakereferansene som en array.
|
|
|
|
Ulempen med denne metoden er at du må undersøke verdiene etterpå for å få
|
|
vite om det matchet eller ikke, og det blir ikke like pent i mine øyne.
|
|
Notasjonen er heller ikke så intuitiv, spør du meg.
|
|
|
|
Jeg ser sjelden denne konstruksjonen i bruk i praksis.
|
|
|
|
\begin{block}{Eksempel}
|
|
my \$var = "en to";\newline
|
|
my (\$a, \$b) = \$var =\~{} /(.*) (.*)/;
|
|
\end{block}
|
|
}
|
|
|
|
\frame
|
|
{
|
|
\frametitle{Bruk av tilbakereferanser i substitusjons-uttrykk}
|
|
|
|
Du kan også bruke \$1 med venner i substitusjoner.
|
|
|
|
\begin{block}{Eksempel}
|
|
\# Dette bytter om på ``en'' og ``to''.\newline
|
|
my \$var = ''en to'';\newline
|
|
\$var =\~{} s/(.*) (.*)/\$2 \$1/;
|
|
\end{block}
|
|
|
|
\begin{block}{PS:}
|
|
En del andre språk, som f.eks. sed og vim, bruker $\backslash$1 i stedet
|
|
for \$1 og så videre.
|
|
\end{block}
|
|
}
|
|
|
|
\frame
|
|
{
|
|
\frametitle{Bruk av tilbakereferanser inne i selve patternet}
|
|
|
|
Om du vil, kan du også bruke tilbakereferanser til en tidligere del av
|
|
patternet lenger ute i regexen. Dette kan for eksempel være kjekt om du
|
|
skal matche en serie av like tegn, men det er mer enn ett alternativ for
|
|
hvilket tegn det er.
|
|
|
|
Når du gjør tilbakereferanser inne i selve patternet, må du bruke
|
|
$\backslash$1 i stedet for \$1 og så videre. \$1 og lignende vil
|
|
\textit{ikke} virke! De er nemlig ikke definert ennå.
|
|
|
|
\begin{block}{Eksempel: Fjerning av trippelkonsonanter}
|
|
my \$var = ''busssjåfør'';\newline
|
|
\$var =\~{} s/(.)$\backslash$1+/\$1\$1/g;
|
|
\end{block}
|
|
}
|
|
|
|
\frame
|
|
{
|
|
\frametitle{Viktig bruksområde: Tainting}
|
|
|
|
Tainted mode er en feature i Perl som tvinger deg til å sjekke inputen din
|
|
til scripts. Det er et nyttig verktøy for å unngå lite kledelige
|
|
sikkerhetshull i webapplikasjoner, for eksempel.
|
|
|
|
Tainted mode virker slik at det eneste du får lov å bruke innleste (ergo
|
|
potensielt skumle) variabler til, er å kjøre regex-matching på dem. Så kan
|
|
du hente ut \$1 med venner derfra, og bruke dem.
|
|
|
|
Tainted mode slås på med opsjonen -T bak perl-programmet.
|
|
}
|
|
|
|
\frame
|
|
{
|
|
\frametitle{Hvordan gruppere uten at innholdet huskes}
|
|
|
|
Om du ønsker å bruke parenteser til gruppering, men \textit{ikke} ønsker å
|
|
bruke innholdet i parentesen etterpå, kan du bruke ``?:'' først i
|
|
parentesen.
|
|
|
|
Dette er spesielt nyttig i split(), men også greit om du har masse
|
|
parenteser du vil huske, men så plutselig noen du ikke vil huske midt inni.
|
|
Det sparer også bittelitt minne, men det er mindre viktig.
|
|
|
|
\begin{block}{Eksempel}
|
|
\# returnerer (``b'', ``a'', ``n'', ``a'', ``n'', ``e'', ``r'')\newline
|
|
my @a = split(/(a|e)/, "bananer");
|
|
|
|
\# returnerer (``b'', ``n'', ``n'', ``r'')\newline
|
|
my @b = split(/(?:a|e)/, "bananer");
|
|
\end{block}
|
|
}
|
|
|
|
\subsection{Mer om tegnklasser}
|
|
|
|
\frame
|
|
{
|
|
\frametitle{Et gjensyn med $[$ og $]$}
|
|
|
|
For en stund siden snakket vi så vidt om $[$ og $]$, som brukes for å
|
|
definere en klasse av tegn. De skal vi snakke litt mer om nå.
|
|
|
|
La oss først huske at en serie med tegn i firkantparenteser betyr at vi
|
|
matcher ett av tegnene i firkantparentesen en gang.
|
|
|
|
\begin{block}{Eksempel}
|
|
\textbf{/$[$abc$]$/} matcher ett tegn, som kan være enten ``a'', ``b''
|
|
eller ``c''.
|
|
\end{block}
|
|
}
|
|
|
|
\frame
|
|
{
|
|
\frametitle{Matching av fleksibelt antall tegn med tegnklasse}
|
|
|
|
Skal vi matche et annet antall tegn enn nøyaktig ett, må vi (som vanlig)
|
|
bruke en kvantifikator som f.eks. *, +, ? eller \{m,n\}.
|
|
|
|
\begin{block}{Eksempel}
|
|
\textbf{/$[$abc$]$+/} matcher ett eller flere tegn, som kan være enten
|
|
``a'', ``b'' eller ``c''.
|
|
\end{block}
|
|
}
|
|
|
|
\frame
|
|
{
|
|
\frametitle{Tegnsekvenser}
|
|
|
|
Siden det er litt tiltak å skrive tegnklasser som for eksempel
|
|
\textbf{$[$0123456789$]$}, \textbf{$[$abcdef$]$} og sånt, har man mulighet
|
|
for å angi intervaller. Dette gjøres med bindestrek.
|
|
|
|
\begin{tabular}{|l|l|}\hline
|
|
\textbf{Sekvens} & \textbf{Tilsvarer} \\ \hline
|
|
\textbf{$[$0-9$]$} & \textbf{$[$0123456789$]$} \\
|
|
\textbf{$[$a-f$]$} & \textbf{$[$abcdef$]$} \\
|
|
\textbf{$[$0-9a-f$]$} & \textbf{$[$0123456789abcdef$]$} \\
|
|
\hline
|
|
\end{tabular}
|
|
|
|
\begin{block}{Eksempel}
|
|
\textbf{/\^{}0x$[$0-9a-fA-F$]$+\$/} matcher et hexadecimalt tall, som for
|
|
eksempel ``0x1337babe''.
|
|
\end{block}
|
|
}
|
|
|
|
\frame
|
|
{
|
|
\frametitle{Negasjon}
|
|
|
|
Av og til ønsker du å matche alt unntatt visse tegn, som for eksempel alt
|
|
unntatt kolon. For å gjøre dette kan du bruke en negert tegnklasse. Dette
|
|
gjøres ved å sette tegnet \^{} (hatt) fremst i tegnklassen.
|
|
|
|
Det stemmer, hatt betyr noe helt annet inni en tegnklasse enn den betyr
|
|
utenfor. Litt forvirrende i starten, men det går fort over.
|
|
|
|
\begin{block}{Eksempel}
|
|
\textbf{/\^{}($[$\^{}:$]$*)/} matcher fra starten av strengen til første
|
|
kolon (eller slutten av strengen, om det ikke finnes noe kolon i den) og
|
|
legger resultatet i \$1.
|
|
\end{block}
|
|
}
|
|
|
|
\frame
|
|
{
|
|
\frametitle{Escaping av spesielle tegn i tegnklasser}
|
|
|
|
Siden \^{} (hatt), - (bindestrek) og $]$ har spesiell betydning i en
|
|
tegnklasse ($]$ avslutter klassen), må man escape dem om man vil ha et
|
|
bokstavelig tegn.
|
|
|
|
Dette kan gjøres enten ved å escape dem med backslash, eller ved å plassere
|
|
dem spesielle steder i tegnklassen. I praksis brukes ofte spesiell
|
|
plassering, siden dette er vanlig i andre regex-implementasjoner.
|
|
|
|
\begin{tabular}{|l|l|} \hline
|
|
\textbf{Tegn} & \textbf{Plassering for å escape} \\ \hline
|
|
\^{} (hatt) & Et annet sted enn fremst \\
|
|
- (bindestrek) & Helt bakerst eller helt fremst (bak eventuell hatt) \\
|
|
$]$ & Helt fremst (bak eventuell hatt) \\
|
|
\hline
|
|
\end{tabular}
|
|
}
|
|
|
|
\frame
|
|
{
|
|
\frametitle{Ferdigdefinerte tegnklasser}
|
|
|
|
Perl har også en del ferdigdefinerte tegnklasser. Disse kan brukes enten som byggestener i egendefinerte tegnklasser, eller for seg selv.
|
|
|
|
Eksemplene nedenfor benytter $\backslash$w, som matcher tegn som kan være i
|
|
ord (det vil si bokstaver, tall og \_ (underscore)).
|
|
|
|
\begin{block}{Eksempler}
|
|
\textbf{/($\backslash$w+)/} henter ut det første ordet og legger det i \$1.
|
|
|
|
\textbf{/($[$$\backslash$w-$]$+)/} gjør som forrige eksempel, men tillater
|
|
bindestrek i ordet.
|
|
\end{block}
|
|
}
|
|
|
|
\frame
|
|
{
|
|
\frametitle{Liste over ferdigdefinerte tegnklasser}
|
|
|
|
Dette er en liten oversikt over de mest brukte ferdigdefinerte
|
|
tegnklassene. Det finnes en del flere, men de er ikke like vanlige. Du
|
|
finner dem i ``perldoc perlre''.
|
|
|
|
\begin{tabular}{|l|l|}\hline
|
|
\textbf{Klasse} & \textbf{betydning} \\ \hline
|
|
$\backslash$w & Match a "word" character (alphanumeric plus ``\_'')\\
|
|
$\backslash$W & Match a non-"word" character\\
|
|
$\backslash$s & Match a whitespace character\\
|
|
$\backslash$S & Match a non-whitespace character\\
|
|
$\backslash$d & Match a digit character\\
|
|
$\backslash$D & Match a non-digit character\\
|
|
$\backslash$b & Match a word boundary\\
|
|
\hline
|
|
\end{tabular}
|
|
}
|
|
|
|
\section{Eksempler og avslutning}
|
|
|
|
\subsection{Eksempler}
|
|
|
|
\frame
|
|
{
|
|
\frametitle{Eksempel: Strippe HTML}
|
|
|
|
Dette eksempelet har dere sikkert sett før, på plakaten til kurset. Nå har
|
|
dere sikkert lært nok til å se hva den gjør også!
|
|
|
|
Merk at om du leser inn en linje om gangen vil ikke denne koden støtte
|
|
html-tags som går over flere linjer.
|
|
|
|
Vi ser at uttrykket matcher en <, så innholdet i tagen (ett eller flere
|
|
tegn som ikke er >, og så til slutt >. Substitusjonen er også global, så
|
|
alle treffene blir erstattet med en tom streng.
|
|
|
|
\begin{block}{Eksempel}
|
|
\textbf{s/<$[$\^{}>$]$+>//g}
|
|
\end{block}
|
|
}
|
|
|
|
\frame
|
|
{
|
|
\frametitle{Eksempel: Hente ut linker fra HTML}
|
|
|
|
Om du vil hente ut alle linkene i et HTML-dokument, kan du for eksempel
|
|
matche på det som er i hermetegn bak ``href=''.
|
|
|
|
\textbf{$\backslash$s*} er med her og der fordi det er valgfritt med mellomrom der.
|
|
|
|
\begin{block}{Eksempel}
|
|
\textbf{/href$\backslash$s*=$\backslash$s*''([\^{}'']+)/i}
|
|
\end{block}
|
|
|
|
\begin{block}{I praktisk bruk}
|
|
\textbf{perl -ne 'print ''\$1$\backslash$n'' if /href$\backslash$s*=$\backslash$s*''([\^{}'']+)/i' fil.html}
|
|
\end{block}
|
|
}
|
|
|
|
\frame
|
|
{
|
|
\frametitle{Eksempel: Hente ut flere linker per linje fra HTML}
|
|
|
|
Eksempelet på forrige slide hadde en begrensning: Det støttet bare en link
|
|
per linje. Det er sikkert fullt mulig å lage en regex som støtter å fiske
|
|
ut flere, og legge dem i \$1, \$2 og så videre, men det blir bare tungvint
|
|
og ekkelt. I stedet kan vi enten modifisere input-dataene (f.eks. legge inn
|
|
linjeskift mellom alle HTML-tags) eller kjøre flere matchinger per linje.
|
|
Vi satser på å gjøre flere matchinger per linje.
|
|
|
|
Her har vi samme regex som i stad, men substituerer bort patternet i stedet
|
|
for å matche det. Slik holder vi på til det ikke er mer som matcher.
|
|
|
|
\begin{block}{Eksempel}
|
|
\textbf{perl -ne 'print ''\$1$\backslash$n'' while s/href$\backslash$s*=$\backslash$s*''([\^{}'']+)//i' fil.htm}
|
|
\end{block}
|
|
}
|
|
|
|
\frame
|
|
{
|
|
\frametitle{Eksempel: Matche primtall}
|
|
|
|
Følgende kode matcher et primtall. Den er vel kanskje ikke kjempe-intuitiv,
|
|
men virker for små primtall. For store primtall går den kjapt tom for minne
|
|
og tryner.
|
|
|
|
Dette er for øvrig omtrent tregest tenkelige måte å regne ut om noe er
|
|
primtall eller ikke, så den bør kun brukes som kuriositet ;-)
|
|
|
|
Den fungerer ved at man først matcher to eller flere tegn, før man matcher
|
|
denne sekvensen to eller flere ganger. I praksis bruteforcer man altså om
|
|
tallet er delelig med noe.
|
|
|
|
\begin{block}{Eksempel}
|
|
perl -e '(1x\$ARGV$[$0$]$) =\~{} /\^{}(..+?)$\backslash$1+\$/ || print ``prime$\backslash$n'''
|
|
\end{block}
|
|
}
|
|
|
|
\subsection{Avslutning}
|
|
|
|
\frame
|
|
{
|
|
\frametitle{Generelle triks}
|
|
|
|
Når du skriver en regex er det noen få triks som gjerne sparer deg for en
|
|
del hodebry:
|
|
|
|
\begin{itemize}
|
|
\item Gjør den enkel først, legg på særtilfeller etter hvert.
|
|
\item Test at den gjør det du vil underveis. Det er fort gjort å snike inn
|
|
en skrivefeil som ødelegger alt.
|
|
\item Om regexen blir kjempediger og fæl, stopp og tenk på hvorvidt dette
|
|
egentlig var rett verktøy å bruke. Kanskje du bør lage en skikkelig
|
|
parser i stedet?
|
|
\item Av og til kan det være kjekt å først matche på et grovt pattern,
|
|
hente ut det som er interessant, og så matche videre med en ny regex.
|
|
\end{itemize}
|
|
}
|
|
|
|
\frame
|
|
{
|
|
\frametitle{Hva er Regex IKKE bra til?}
|
|
|
|
Nå som vi har sett at regex gjør det mulig å gjøre masse flott, er det kanskje en plan å se litt på hva det \textit{ikke} passer så bra til.
|
|
|
|
\begin{itemize}
|
|
\item Regex støtter ikke rekursjon. Dette gjør det håpløst å matche f.eks.
|
|
HTML skikkelig, siden vi ikke kan holde styr på start- og slutt-tager.
|
|
\item Om teksten er litt komplisert er det ofte vel så greit å skrive en
|
|
skikkelig parser, i stedet for en supergrisete regex.
|
|
\item Regex er ikke veldig maintainable, spesielt ikke om du overdriver.
|
|
\end{itemize}
|
|
}
|
|
|
|
\frame
|
|
{
|
|
\frametitle{Hvordan lære mer?}
|
|
|
|
Om du vil lære mer, er det selvsagt et triks å bruke regex en del. Selv
|
|
bruker jeg bortimot daglig regex til et eller annet, som regel i
|
|
forbindelse med små onelinere jeg bruker for å massere tekst. Jeg bruker
|
|
det også mye for å gjøre søk og erstatt i vim (jeg har brukt en del regexer
|
|
i vim mens jeg skrev disse foilene).
|
|
|
|
Om du vil lese mer, er kommandoen ``perldoc perlre'' veldig fin. O'Reilly
|
|
har også noen bøker jeg tror er bra, og et par av dem ligger sikkert på
|
|
PVV.
|
|
|
|
Har man et obskurt problem er det også ofte hjelp å få på IRC, eventuelt på
|
|
mailinglister med nerder på (som f.eks. aktive@pvv.ntnu.no).
|
|
}
|
|
|
|
\end{document}
|