Perl/Espressioni regolari (oltre)
Le espressioni regolari hanno un livello di astrazione in più consentendo di concentrarsi su contenuti "generici" e non solo su quelli noti a priori.
Prima , Match , Dopo
[modifica | modifica sorgente]Quando chiediamo di fare un'operazione di match, il Perl mette a disposizione tre variabili che consentono di capire meglio "cosa" si è catturato: $` contiene quello che c'è prima $& contiene il match $' contiene quello che c'è dopo
riprendendo l'esempio relativo alla parola "vita" vediamo cosa succede in questo caso:
foreach $stringa ( "che vita... stare in pace con il mondo" , "mi sembra che sia vitale respirare" , "sono contento della mia vita!" , "ma ti sembra vita??" , "non so se la mia vita mi piace molto" ) { if ( $stringa =~ m/\s+vita[\s.?!]+/ ) { print "$stringa fa match!\n"; print "prima :$`\n"; print "match :$&\n"; print "dopo :$'\n"; print "---\n"; } }
che produce:
che vita... stare in pace con il mondo fa match! prima :che: match : vita... : dopo :stare in pace con il mondo: --- sono contento della mia vita! fa match! prima :sono contento della mia : match : vita!: dopo :: --- ma ti sembra vita?? fa match! prima :ma ti sembra : match : vita??: dopo :: --- non so se la mia vita mi piace molto fa match! prima :non so se la mia: match : vita : dopo :mi piace molto: ---
Raggruppare
[modifica | modifica sorgente]Oltre alle variabili precedenti le espressioni regolari consentono di fare dei raggruppamenti: considerare un certo "gruppo di sintassi" come un unico elemento. La sintassi per specificare questa possibilità è mettere la sintassi fra '(' e ')'. Vediamo allora come migliorare l'esempio precedente aggiungendo il caso di estrarre anche " vitavita " , " vitavita!! " e " vitavita? ". L'espressione regolare in questo caso diventa : "\s+(vita)+[\s.!?]+" ciò che si trova fra parentesi viene trattato dal moltiplicatore come se fosse un'unica entità.
foreach $stringa ( "che vita... stare in pace con il mondo" , "mi sembra che sia vitale respirare" , "sono contento della mia vita!" , "ma ti sembra vita??" , "non so se la mia vita mi piace molto" , "non so se la mia vitavita mi piace molto" , "non so se vitavitavita sia corretta" ) { if ( $stringa =~ m/\s+(vita)+[\s.!?]+/ ) { print "$stringa fa match!\n"; print "prima :$`:\n"; print "match :$&:\n"; print "dopo :$':\n"; } }
che produce:
che vita... stare in pace con il mondo fa match! prima :che: match : vita... : dopo :stare in pace con il mondo: sono contento della mia vita! fa match! prima :sono contento della mia: match : vita!: dopo :: ma ti sembra vita?? fa match! prima :ma ti sembra: match : vita??: dopo :: non so se la mia vita mi piace molto fa match! prima :non so se la mia: match : vita : dopo :mi piace molto: non so se la mia vitavita mi piace molto fa match! prima :non so se la mia: match : vitavita : dopo :mi piace molto: non so se vitavitavita sia corretta fa match! prima :non so se: match : vitavitavita : dopo :sia corretta:
Raggruppare ed usare ciò che si è raggruppato
[modifica | modifica sorgente]Se non indicato esplicitamente il Perl mette da qualche parte in memoria i vari raggruppamenti. Questi possono essere usati nella stessa espressione regolare oppure "subito" dopo l'azione di match. Ogni elemento raggruppato viene numerato a partire da 1. Se si usano nell'espressione regolare occorre indicarli come \X dove X è una cifra. Se si usano fuori dall'espressione regolare allora vengono memorizzati in $X dove X è una cifra. Vediamo un esempio: voglio sapere se la parola "stringa" è presente due volte all'interno della frase. L'espressione regolare potrebbe essere la seguente: "(stringa).*\1"
$stringa = "questa è la stringa di test e rimane una stringa !";
if ( $stringa =~ /(stringa).*\1/ ) { print " mi interessa $1 \n"; }
che produce:
mi interessa stringa
Vediamo ora un caso più complesso : voglio sapere se una parola di almeno 5 caratteri è ripetuta in una frase e quale è (per ora ci si ferma alla prima che si incontra). L'espressione regolare potrebbe essere la seguente:"(\w{5,}).*\1"
# commentate una stringa e provate ! $stringa = "questa è la stringa di test e rimane una stringa !"; $stringa = "questa \350 la stringa di test e rimane una stringa questa !"; if ( $stringa =~ /(\w{5,}).*\1/ ) { print "ho trovato:$1: \n"; }
che produce:
ho trovato:questa:
Raggruppare ma NON usare ciò che si è raggruppato
[modifica | modifica sorgente]Per non usare il raggruppamento (libera memoria e semplifica la ricerca) è sufficiente usare la sintassi : "(?:" e ")". Cioè basta aggiungere subito dopo la parentesi "(" un "?:".
$stringa = "io contengo una parola di soli quattro caratteri";
if ( $stringa =~ m/\s(?:\w{4,4})\s/ ) { print "la stringa -$stringa- contiene una parola di 4 caratteri!\n"; print "parola è $&\n"; }
L'uso del raggruppamento senza memorizzare non modifica le altre caratteristiche del raggruppamento. Per esempio voglio parole di 4 o 8 o 12 ... etc
$stringa = "io contengo una parola di soli quattro caratteri";
if ( $stringa =~ m/\s(?:\w{4,4})+\s/ ) { print "la stringa -$stringa- contiene una parola multipla di 4 caratteri!\n"; print "parola è $&\n"; }
che produce:
la stringa -io contengo una parola di soli quattro caratteri- contiene una parola multipla di 4 caratteri! parola è contengo
Posizionamento della ricerca
[modifica | modifica sorgente]Se si vuole cercare qualcosa che si trova all'inizio della stringa occorre mettere come primo carattere il carattere ^. Se si vuole cercare qualcosa che si trova alla fine della stringa occorre mettere come ultimo carattere il carattere $. Se volessi cercare la parola vita all'inizio della stringa (ma non in mezzo!) l'espressione regolare potrebbe essere: "^vita" Vediamo un esempio:
foreach $stringa ( "che vita... stare in pace con il mondo" , "mi sembra che sia vitale respirare" , "sono contento della mia vita!" , "vita : ma ti sembra vita??" , "non so se la mia vita mi piace molto" , "non so se la mia vitavita mi piace molto" , "non so se vitavitavita sia corretta" ) { if ( $stringa =~ m/^vita/ ) { print "$stringa fa match!\n"; print "prima :$`:\n"; print "match :$&:\n"; print "dopo :$':\n"; } }
che produce:
vita : ma ti sembra vita?? fa match! prima :: match :vita: dopo : : ma ti sembra vita??:
Se volessi cercare la parola vita alla fine della stringa (ma non in mezzo!) l'espressione regolare potrebbe essere : "vita[?!.]*$" NOTA:si è messa la punteggiatura finale che può non esserci, esserci , ed essere ripetuta !
foreach $stringa ( "che vita... stare in pace con il mondo" , "mi sembra che sia vitale respirare" , "sono contento della mia vita!" , "vita : ma ti sembra vita??" , "non so se la mia vita mi piace molto" , "non so se la mia vitavita mi piace molto" , "non so se vitavitavita sia corretta" ) { if ( $stringa =~ m/vita[?!.]*$/ ) { print "---\n"; print "$stringa fa match!\n"; print "prima :$`:\n"; print "match :$&:\n"; print "dopo :$':\n"; } }
che produce:
--- sono contento della mia vita! fa match! prima :sono contento della mia : match :vita!: dopo :: --- vita : ma ti sembra vita?? fa match! prima :vita : ma ti sembra : match :vita??: dopo ::
Separatore di parola
[modifica | modifica sorgente]In questa sezione si parla del separatore di parola. Con questo termine si indica il punto di inizio/fine di una parola. Negli esempi precedenti si è utilizzato lo spazio come elemento che separa una parola dalla successiva. Esiste un marcatore "più" preciso : \b indica la sequenza \W\w o \w\W e si posiziona "nel mezzo". \B è tutto quello che non è il \b
Occorre però capirne bene la sua utilità. Con il separatore la parola viene già considerata e separata ( caso di /b ) o considerata e non separata ( caso /B ) dal resto delle parole. Infatti prendendo il punto in cui si verifica \w\W o \W\w la si è già considerata come estranea ed appartenente ad una certa sequenza. Vediamo se con un esempio si chiarisce questo: applichiamolo alla ricerca della parola "vita" nell'esempio precedente:
foreach $stringa ( "che vita... stare in pace con il mondo" , "mi sembra che sia vitale respirare" , "sono contento della mia vita!" , "ma ti sembra vita??" , "non so se la mia vita mi piace molto" , "non so se la mia vitavita mi piace molto" , "non so se vitavitavita sia corretta" ) { if ( $stringa =~ m/\b(vita)+\b/ ) { print "---\n"; print "$stringa fa match!\n"; print "prima :$`:\n"; print "match :$&:\n"; print "dopo :$':\n"; } }
che produce:
--- che vita... stare in pace con il mondo fa match! prima :che : match :vita: dopo :... stare in pace con il mondo: --- sono contento della mia vita! fa match! prima :sono contento della mia : match :vita: dopo :!: --- ma ti sembra vita?? fa match! prima :ma ti sembra : match :vita: dopo :??: --- non so se la mia vita mi piace molto fa match! prima :non so se la mia : match :vita: dopo : mi piace molto: --- non so se la mia vitavita mi piace molto fa match! prima :non so se la mia : match :vitavita: dopo : mi piace molto: --- non so se vitavitavita sia corretta fa match! prima :non so se : match :vitavitavita: dopo : sia corretta:
NOTA: Avete notato che ora "vita" viene identificata senza spazi? (confrontate l'inizio e la fine del carattere ":" in prima , match , dopo )
Guardare avanti
[modifica | modifica sorgente]In inglese "lookahead" significa che durante la verifica dell'espressione regolare ne viene verificata la contemporaneità di un'altra (positivo) oppure la non contemporaneità di un'altra (negativo). La sintassi è la seguente : (?= ) per la positiva; (?! ) per la negativa. Come per (?: ) non viene memorizzata in uno spazio in memoria, ma fa parte del match. Vediamo questo esempio :
foreach $stringa ( "che vita... stare in pace con il mondo" , "mi sembra che sia vitale respirare" , "sono contento della mia vita!" , "ma ti sembra vita??" , "non so se la mia vita mi piace molto" , "non so se la mia vitavita mi piace molto" , "non so se vitavitavita sia corretta" ) { if ( $stringa =~ m/(vita)+(?=[!?.])/ ) { print "---\n"; print "$stringa fa match!\n"; print "prima :$`:\n"; print "match :$&:\n"; print "dopo :$':\n"; } }
che produce :
--- che vita... stare in pace con il mondo fa match! prima :che : match :vita: dopo :... stare in pace con il mondo: --- sono contento della mia vita! fa match! prima :sono contento della mia : match :vita: dopo :!: --- ma ti sembra vita?? fa match! prima :ma ti sembra : match :vita: dopo :??:
Cioè solo tutte quelle righe che che hanno un carattere .,!,? dopo la sequenza v-i-t-a.
Alternativa
[modifica | modifica sorgente]Si è visto nel caso dei caratteri che era possibile specificare dei caratteri diversi mettendoli fra []. Nel caso di espressioni complesse invece viene incontro il simbolo |. Attenzione che questo NON si riferisce al singolo carattere ma al raggruppamento nel quale è usato. Se per esempio voglio tutte le frasi con la parola vita o morte posso usare la seguente espressione regolare: "vita|morte"
foreach $stringa ( "che vita... stare in pace con il mondo" , "purché non sia un portatore di morte" , "mi sembra che sia vitale respirare" ) { if ( $stringa =~ m/\bvita\b|\bmorte\b/ ) { print "---\n"; print "$stringa fa match!\n"; print "prima :$`:\n"; print "match :$&:\n"; print "dopo :$':\n"; } }
che produce :
--- che vita... stare in pace con il mondo fa match! prima :che : match :vita: dopo :... stare in pace con il mondo: --- purché non sia un portatore di morte fa match! prima :purché non sia un portatore di : match :morte: dopo ::
vediamo un altro esempio : occorre che la parola vita sia all'inizio e la parola morte sia in fondo e viceversa
foreach $stringa ( "vita precede la morte" , "morte segue la vita" , "morte non segue ne precede morte" , "vita genera vita" ) { if ( $stringa =~ m/^vita.*morte$|^morte.*vita$/ ) { print "---\n"; print "$stringa fa match!\n"; print "prima :$`:\n"; print "match :$&:\n"; print "dopo :$':\n"; } }
che produce :
--- vita precede la morte fa match! prima :: match :vita precede la morte: dopo :: --- morte segue la vita fa match! prima :: match :morte segue la vita: dopo ::
Sii minimale
[modifica | modifica sorgente]Quando si usano i moltiplicatori di caratteri, il Perl cerca la stringa che maggiormente di adatta alla sequenza cercata. A volte questo comportamento non è esattamente quello desiderato. Per dare istruzioni di come cercare si usa la sintassi "?" posta dopo il ripetitore. Vediamone la sintassi: {min,max}? inizia dal numero minimo e poi incrementa fino al massimo {min,}? inizia dal numero minimo e poi incrementa {max}? prova max (non cambia!)
- ? inizia da 0 e poi incrementa
+? inizia da 1 e poi incrementa ?? inizia da 0 e poi incrementa
Vediamo un esempio : data una stringa contenente 'elemento="xxxx"', ripetuto più volte , estrarre i valori fra " e ". Di primo impulso verrebbe l'espressione regolare : "elemento=["].*["]" tuttavia dato che .* inizia dalla maggior numero e poi decrementa , mangia anche i caratteri che seguono, che diventano inutili. Allora si utilizza il modo minimale che consente di dare visibilità anche ai caratteri che seguono. Quindi si potrebbe utilizzare l'espressione regolare : "elemento=["].*?["]"
$stringa = 'elemento="questo è il primo elemento",elemento="questo è il secondo elemento"'; if ( $stringa =~ m/elemento=["].*?["]/ ) { print "---\n"; print "fa match!\n"; print ":$&:\n"; }
che produce :
--- fa match!
- elemento="questo è il primo elemento":
Al lettore che non gli è chiaro : provi a togliere il ? e a capire cosa accade !
gli scalari come espressioni regolari
[modifica | modifica sorgente]È possibile scrivere una espressione regolare in una variabile e poi sostituirla nel match oppure mettere una variabile nell'espressione regolare. Vediamo come modificare l'esempio precedente che cerca la stessa parola nella stringa:
$stringa = "questa è la stringa di test e rimane una stringa questa !"; foreach $prova ( '\w{5,}' , 'stringa' , 'questa' ) { if ( $stringa =~ /($prova).*\1/ ) { print "parola doppia: $1 \n"; } else { print "non trovata $prova ! \n"; } }
che produce :
parola doppia :questa parola doppia :stringa parola doppia :questa
se si ha l'intenzione di sostituire per intero l'espressione regolare dato che in assegnamento di variabile i caratteri speciali vengono interpretati occorre scriverli con l'apice singolo o con il "marcatore" qr che è quello dedicato.
Vediamo l'esempio precedente:
$stringa = "questa \350 la stringa di test e rimane una stringa questa !"; $p1 = "stringa"; foreach $prova ( qr(\w{5,}) , qr($p1) , qr(questa) ) { if ( $stringa =~ /($prova).*\1/ ) { print "parola doppia: $1 \n"; } else { print "non trovata $prova ! \n"; } }
commenti (in linea)
[modifica | modifica sorgente]Dato che le espressioni regolari possono essere molto complicate, segnarsi degli appunti, per quando eventualmente dovranno essere modificate, potrebbe tornare utile. Per poter far questo basta usare la sintassi (?# ... ). Per esempio : (?# ATTENZIONE: questo è un commento per i posteri !)
Scorciatoie (lettera in fondo)
[modifica | modifica sorgente]Spesso complicati concetti possono esprimersi con una lettera in una posizione particolare. Vediamo aggiungendo una (o più) lettera in fondo alla sintassi /<espressione regolare>/ che possibilità offrono in più le espressioni regolari.
Sia minuscolo , sia maiuscolo
[modifica | modifica sorgente]Con la lettera "i" si intendono per tutta l'espressione regolare sia lettere maiuscole che minuscole. Per esempio : voglio sapere se hai digitato "si" maiuscolo o minuscolo. L'espressione regolare potrebbe essere : /si/i
foreach $s ( "Si" , "SI" , "sI" , "si" , "No" ) { if ( $s =~ m/si/i ) { print "$s fa match con /si/i\n"; } else { print "$s NON fa match con /si/i\n"; } }
che produce:
Si fa match con /si/i SI fa match con /si/i sI fa match con /si/i si fa match con /si/i No NON fa match con /si/i
Commenti (su più linee)
[modifica | modifica sorgente]Con la lettera "x" si possono mettere le espressioni regolari su più linee mettendo il commento "perl" in modo tradizionale con il simbolo # . Vediamo questo esempio:
foreach $s ( "Si" , "SI" , "sI" , "si" , "No" , "ni" ) { if ( $s =~ m/ si |#accetta Si ni #accetta Ni /ix ) { print "$s fa match \n"; } else { print "$s NON fa match \n"; } }
NOTA: Gli spazi sono IGNORATI pertanto occorrerà renderli esplici usando \s o [ ].
che produce :
Si fa match SI fa match sI fa match si fa match No NON fa match ni fa match