Perl/Espressioni regolari
Le espressioni regolari (regular expression) sono un utilissimo strumento che lavora sulle stringhe.
È in grado di verificare che una certa sequenza di caratteri sia presente da qualche parte all'interno di una stringa.
In inglese questa operazione si dice di "match".
Quindi se la stringa corrisponde alla espressione regolare vuol dire che "fa match" ( cioè corrisponde ). Ovviamente per fare una azione così complessa, le espressioni regolari utilizzano una sintassi specifica ampiamente definita e documentata da "tomi" di libri.
In Perl le espressioni regolari sono parte integrante del linguaggio.
In modo esplicito vengono identificate come "m/<espressione regolare>/".
I caratteri "/" possono essere sostituiti a piacere con # , | , %.
Salvo casi particolari è normale incontrarle con il carattere "/".
Come si è accennato precedentemente si applicano alle stringhe pertanto la sintassi completa si mostra come segue:
$stringa =~ m/<espressione regolare>/ ;
oppure
$stringa =~ /<espressione regolare>/ ;
da notare l'operatore =~ che indica l'operarazione di match e da NON confondere con = o == !
L'operazione in sé ritorna un vero o falso e non fornisce alcun dato della stringa. Nella sintassi delle espressioni regulari è poi possibile estrarre delle informazioni. Iniziamo con l'operazioni più semplice.
Un carattere
modificaVerifica di un carattere
modificaPer la verifica di un carattere in una stringa la sintassi prevede che questo venga semplicemente indicato. Quindi l'espressione regolare è il carattere stesso. Per esempio se si volesse trovare il carattere g l'espressione regolare è : "g" Esempio :
$stringa = "io sono una stringa";
if ( $stringa =~ m/g/ )
{
print qq(la stringa "$stringa" contiene il carattere 'g'\n);
}
Verifica di un valore decimale o un carattere speciale
modificaCi sono nelle stringhe dei caratteri speciali ( tabulazione, beep, etc ) che vengono usati nei dati ed hanno un significato particolare. Questi caratteri ( dai tempi delle stampanti ) vengono chiamati caratteri di controllo. Dato che alcuni sono molto frequenti, gli sono stati dati dei "simboli" particolari, ma in linea generale questi sono indicati come \cX dove X rappresenta il carattere (la parte \c indica di controllo). Ci sono dei caratteri dei quali non occorre conoscere il corrispondente carattere di controllo perché sono rappresentati da lettere:
\a rappresenta il beep \r rappresenta il ritorno del carrello ( \cM ) \n rappresenta il ritorno a capo \t rappresenta la tabulazione orrizzontale \e rappresenta il carattere di escape
Oltre ai caratteri di controllo è possibile indicare dei valori corrispondenti ai valori ascii della stringa. Questi sono preceduti da \ e seguiti dalla notazione ottale i esadecimale del numero. Nel caso di notazione ottale basta solo il numero a due o tre cifre , mentre nel caso di notazione esadecimale si utilizza x seguito da due o tre cifre. Esempio : decimale 64 , ottale 100, esadecimale 40 per le espressioni regolari questi numeri sono rappresentati come \100 in notazione ottale e \x40 in notazione esadecimale.
Esempio :
$stringa = "\a mamma mia !\n";
if ( $stringa =~ m/\a/ )
{
print " stringa contiene il beep \n";
}
Sintassi per identificare i caratteri
modificaPer fare in modo da identificare meglio i caratteri esiste una sintassi apposta:
. identifica un qualsiasi carattere (in quella posizione) escluso il \n
\d identifica una qualsiasi cifra da 0 a 9. Ma una sola cifra (un solo carattere) e non un numero per intero composta da più cifre.
\w qualsiasi carattere fra "a" e "z" , "A" a "Z" , qualsiasi cifra da 0 a 9 ed il carattere '_'.Ma un solo carattere non una parola
\s identifica un carattere spazio fra " " , \t , \n , \f , \r
\D qualsiasi carattere tranne quelli di \d
\W qualsiasi carattere tranne quelli di \w
\S qualsiasi carattere tranne quelli di \s
Sintassi per identificare dei caratteri specifici
modificaPer fare in modo di verificare soltanto che si abbiano alcuni caratteri (in una certa posizione) si possono indicare fra '[' e ']'. Per esempio se si vuole trovare il singolare o il plurale di casa : casa , case. Queste due parole differiscono solo per l'ultima vocale "a" ed 'e'. Quindi l'espressione regolare è : "cas[ae]" Esempio :
$stringa = "questa stringa contiene villette";
if ( $stringa =~ m/cas[ae]/ )
{
print "la stringa $stringa contiene la sequenza 'cas[ae]' \n";
}
ovviamente "villetta" è un tipo di casa ma non è la sequenza c-a-s-a oppure la sequenza c-a-s-e ! Nel raggruppamento è possibile includere TUTTI i tipi di caratteri fin qui indicati e quindi anche gli speciali.
Se invece non si vogliono certi caratteri è sufficiente mettere ^ all'interno di '[' e ']'. Quindi questa stringa farà match :
$stringa = "il contante viene anche chiamato cash";
if ( $stringa =~ m/cas[^ae]/ )
{
print "la stringa $stringa contiene la sequenza 'cas[^ae]' \n";
}
Infatti c-a-s-h soddisfa l'espressione c-a-s- ne "a" ne 'e'
Sintassi per identificare i caratteri (continui)
modificaSe si deve indicare tutti i caratteri minuscoli occorrerebbe scrivere qualcosa tipo:
[abcdefghilmnopqrstuvzxywj]
questo crea i seguenti problemi:
- problema di digitazione ( che ci si dimentica qualche carattere )
- lunghezza dell'espressione regolare (stiamo trattando un solo carattere ! pensare ad una parola di 4 caratteri o 10 !)
Per evitare una forma che complichi il processo di comprensione sono stati utilizzati le sintassi a range: con l'espressione [a-z] si intendono tutti i caratteri (codifica ascii) che vanno dalla "a" alla "z". Da notare che in codifica ascii questi sono tutti contigui. La stessa cosa vale per i caratteri maiuscoli, e i numeri che diventano rispettivamente [A-Z] e [0-9]. Se per esempio volessimo controllare che un numero esadecimale (di una cifra) sia scritto correttamente dovremmo controllare che abbia i seguenti caratteri: [0123456789abcdefABCDEF] che utilizzando la "forma continua" possono essere compattatati come 0-9 per i numeri e a-f o A-F per le lettere.
$numero_esa = "a";
if ( $stringa =~ m/[0-9a-fA-F]/ )
{
print "$numero_esa è una cifra esadecimale \n";
}
Vediamo ora la rappresentazione delle abbreviazioni viste poco prima rispetto al raggruppamento:
\d è una cifra equivale a [0-9]
\s è un carattere di spaziatura equivale a [\ \t\r\n\f]
\w e un carattere per una parola più _ equivale a [0-9a-zA-Z_]
\D è tutto quello che non è il \d equivale a [^0-9]
\S è tutto quello che non è il \s equivale a [^\s]
\W è tutto quello che non è il \w equivale a [^\w]
Prestare attenzione che l'espressione di prima ([0-9a-fA-F]) non può essere sostituita all'interno delle parentesi [] da nessun simbolo.
Verifica di una sequenza di caratteri
modificaSequenza di caratteri (nota)
modificaPer la verifica di una sequenza di caratteri (parola) basta semplicemente indicarli nella stessa sequenza desiderata. Per esempio la parola "sono" è composta da una s due o una n ma nella sequenza "s" seguita da "o" seguita da "n" seguita da "o" e la si vuole esattamente in questa sequenza (s-o-n-o). Quindi l'espressione regolare per identificare la parola "sono" è : "sono" Esempio :
$stringa = "io sono una stringa";
if ( $stringa =~ m/sono/ )
{
print "la stringa $stringa contiene la sequenza "sono" \n";
}
Notare che la sequenza s-o-n-o può essere ovunque nella stringa :
$stringa = "questa diventerà una prova sonora \n";
if ( $stringa =~ m/sono/ )
{
print "la stringa $stringa contiene la sequenza "sono" \n";
}
infatti sonora contiene la sequenza s-o-n-o(ra). NOTA: se si volesse solo la parola "sono" è sufficiente aggiungere alla espressione regolare di prima un carattere spazio prima e dopo la sequenza "sono":
$stringa = "questa diventerà una prova sonora \n";
if ( $stringa =~ m/ sono / )
{
print "la stringa $stringa contiene la parola "sono" \n";
}
Occorre far notare che è possibile sostituire al carattere indicato una qualsiasi sintassi alternativa vista in precedenza. Quindi in una forma un po' ridondante si potrebbe scrivere l'espressione regolare per " sono " anche nella forma "[ ][s][o][n][o][ ]"
$stringa = "questa diventerà una prova sonora \n";
if ( $stringa =~ m/[ ][s][o][n][o][ ]/ )
{
print "la stringa $stringa contiene la parola "sono" \n";
}
Sequenza di caratteri (ripetizioni)
modificaImmaginiamo ora di dover identificare delle parole che sono state "digitate" due volte consecutivamente. Se per esempio dovessimo trovare la sequenza "ss" o "sss" o "ssss" ... etc. saremmo costretti a scrivere innumerevoli volte tutte le possibili sequenze ! ( probabilmente con cicli molto pesanti! ). Per eliminare questo tipo di possibilità le espressioni regolari mettono a disposizione dei caratteri e una sintassi in grado di indicare le "ripetizioni".
ATTENZIONE: sia la sintassi che i caratteri vengono usati sia per i caratteri che per altre entità delle espressioni regolari. In questa parte ci si riferisce ai caratteri ma tenere presente che possono essere estesi anche ad altri riferimenti. Ciòè hanno una possibilità di impiego che si estende anche oltre i caratteri.
Le "ripetizioni" si riferiscono sempre al carattere immediatamente a sinistra. Vediamo prima le ripetizioni in forma sintattica:
{ min , max } dove min è un numero e rappresenta il numero minimo di ripetizioni e dove max è un numero e rappresenta il numero massimo di ripetizioni.
Quindi l'espressione regolare "s{3,8}" indica il carattere s ripetuto da 3 a 8 volte, è come scrivere un ciclo che verifica: ssssssss sssssss ssssss sssss ssss sss
Se si omette il max (per es. {3,} ) vuol dire che il carattere DEVE essere presente almeno 3 volte e non c'è il limite massimo di 8; se si omette il min (per es. {8} ) vuol dire che il carattere deve essere ripetuto tassativamente 8 volte e quindi è come scrivere "ssssssss".
Vediamo ora i caratteri che indicano alcuni tipi di ripetizioni:
* indica 0 o infinite ripetizioni ed equivale a {0,}
? indica 0 o 1 ed equivale a {0,1}
+ indica 1 o infinite ripetizioni ed equivale a {1,}
È quindi possibile verificare se in una frase è presente una generica parola di 4 caratteri con l'espressione regolare "\s\w{4,4}\s" o anche "\s\w{4}\s".
$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";
}
Se si volesse sapere se in una frase esiste la parola vita seguita da più spazi si potrebbe usare l'espressione regolare "\s+vita\s+\w". Questa individua la sequenza v-i-t-a seguito da uno o più caratteri di spaziatura e un carattere alfanumerico. Quindi fa match con "vita da cani" , "la vita mia " ma non con "vitale è respirare" , "sono contento della mia vita!" , "ma ti sembra vita??". Se si volessero includere questi ultimi casi, sarebbe possibile modificare l'espressione regolare come segue: "\s+vita[\s.?!]+".
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";
}
}
che produce:
che vita... stare in pace con il mondo fa match! sono contento della mia vita! fa match! ma ti sembra vita?? fa match! non so se la mia vita mi piace molto fa match!
Da notare che il carattere . ed il carattere ? è fra le parentesi [] ed è quindi se stesso e non il carattere speciale ( '.' fuori dalle [] indica qualsiasi carattere escluso \n ; '?' fuori dalle [] indica la ripetizione 0 o 1 )!