Perl/Espressioni regolari (oltre)

Wikibooks, manuali e libri di testo liberi.
Indice del libro

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]

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]

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]

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]

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]

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]

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]

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]

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]

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]

È 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]

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]

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]

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]

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