|
Le espressioni regolari sono una
delle migliori caratteristiche del Perl che lo rendono molto
flessibile e anche un po’ particolare. Dato l’uso
intensivo che se ne fa in questo programma, sarà opportuno
cogliere l’occasione per esaminarle in dettaglio. Le
espressioni regolari vengono definite in informatica teorica
come delle espressioni che servono a descrivere insiemi di stringhe.
L’operazione principale che utilizza una espressione regolare è
il matching, cioè la verifica che una stringa appartiene
all’insieme descritto dall’espressione regolare.
In realtà il Perl (come pure
molti altri programmi) utilizzano questo concetto in maniera più
ampia rispetto alla definizione teorica. Innanzitutto il matching non
viene applicato soltanto all’intera stringa per la verifica
della corrispondenza; normalmente invece viene effettuata una ricerca
di sottostringhe che la soddisfino. Naturalmente è possibile
specificare che si vuole un matching sull’intera stringa e non
su una sottostringa (ed è un caso frequente). Ci sono inoltre
altre due varianti di espressione regolare che causano degli effetti
collaterali: questi modificano la stringa su cui si effettua il
matching. Gli effetti collaterali sono sostituzioni di stringhe e
traslazioni, cioè sostituzioni di singoli caratteri e
l’utilissima estrazione di sottostringhe.
Per usare le espressioni regolari si
usa l’operatore =~, il quale applica ad una stringa il
matching e gli effetti collaterali che l’espressione causa. Il
primo operando di questo operatore è una variabile scalare,
mentre il secondo operando è l’espressione regolare vera
e propria, che ha in generale questa forma:
[<prefisso>]<delim><regexp><delim>[<string><delim> [<suffisso>]]
L’operatore ritorna sempre un valore, che può sempre
essere interpretato come vero se l’applicazione
dell’espressione regolare ha avuto successo, falso altrimenti.
Vediamo ora alcuni esempi:
(a) /0/
(b) m/\s*|\w+/
(c) s/[ \t\n]+/ /g
(d) tr/a-z/A-Z/
Il <prefisso> può essere m (matching), s
(sostituzione), tr (traslazione). Le barre / sono
normalmente usate come delimitatori ed hanno una funzione analoga
alle virgolette usate per delimitare una stringa. In realtà è
possibile utilizzare qualsiasi carattere come delimitatore (è
sufficiente che il primo e l’ultimo siano uguali) ma se il
delimitatore non è la barra occorre sempre specificare il
<prefisso>. Se invece il <delim> è
la barra e il <prefisso> è omesso, si assume m.
Il <suffisso> serve per specificare delle opzioni
sull’effetto collaterale causato dalla espressione regolare,
per esempio la ripetizione di una sostituzione, come il (c).
La <regexp> vera e
propria utilizza i caratteri alfanumerici nel loro significato
letterale (per esempio /a/ matcha "a"),
mentre i caratteri non alfanumerici hanno generalmente un significato
speciale, e li chiameremo perciò metacaratteri. Nella
seguente tabella sono
riepilogati i principali metacaratteri e il loro significato.
CARATTERI
. qualsiasi carattere tranne il newline
[a-z] qualsiasi carattere elencato
(il - genera una sequenza, il ^ inverte la classe)
\s spazio bianco
\w una lettera, cifra decimale o _
\d una cifra decimale
\S come [^\s]
\W come [^\w]
\D come [^\d]
ITERATORI
* 0 o più occorrenze
+ 1 o più occorrenze
? 0 o 1 occorrenze
{n} esattamente n occorrenze
{n,} almeno n occorrenze
{n,m} da n a m occorrenze
ALTRO
^ inizio di stringa
$ fine di stringa
| alternative
() raggruppamento
È possibile togliere il significato speciale ad un
metacarattere ed usarlo nel suo significato letterale precedendolo
con un backslash: per esempio /\*/ matcha "*".
Alcuni caratteri alfanumerici invece, se sono preceduti dal backslash
diventano metacaratteri e acquistano un significato speciale che
normalmente non hanno: per esempio /\d/ matcha una cifra
decimale.
Veniamo all’analisi dei
metacaratteri: il punto . sostituisce qualsiasi carattere
eccetto il newline. Le parentesi quadre racchiudono insiemi di
caratteri (le cosiddette classi di caratteri), e matchano una
occorrenza tra i caratteri che racchiudono. Per esempio [azt]
matcha uno tra i caratteri a, z e t. Le classi possono essere negate
([^azt] matcha qualunque carattere che non sia a, z o t) ed è
possibile abbreviare sequenze contigue nel codice ascii di caratteri,
utilizzando il trattino: [0-7] abbrevia [01234567].
Una volta definite le espressioni
che servono a matchare un carattere, introduciamo gli iteratori che
servono a ripetere in vari modi il matching del carattere,
metacarattere, o raggruppamento (vedi dopo) che seguono. Per
esempio /[0-7]/ matcha una cifra ottale; se utilizziamo
l’iteratore +, /[0-7]+/ matcherà "1",
"123", "0765". Gli altri iteratori
sono ?, che rende opzionale ciò che precede (matcha 0 o
1 occorrenza), l’iteratore * che considera da 0
occorrenze in sù, e gli iteratori {n}, {n,},
{n,m} che matchano n, da n o più, da n
a m occorrenze. Naturalmente sarebbe troppo limitante se un
iteratore si potesse applicare soltanto ad un carattere: infatti è
possibile utilizzare le parentesi tonde per raggruppare una sequenza
a cui applicare un iteratore. Per esempio /x([ab]\d)*/ matcha
“xa1”, “xa1b2a3” ed anche “x”.
Si può scrivere una
espressione regolare composta da più sottoespressioni che
matchi una tra varie alternative: questo si ottiene utilizzando
l’alternatore |. Infine è possibile matchare dei
punti in una stringa che non corrispondono necessariamente ad un
carattere: abbiamo ^ che matcha l’inizio di una stringa
e $ che matcha la fine. Per esempio /^a|z$/ matcha ogni
stringa che comincia per a o finisce per z.
Con il semplice matching (m/<regexp>/)
si ricerca una sottostringa corrispondente alla stringa. Con la
sostituzione, che ha la sintassi s/<regexp>/<replacement>/
si sostituisce la stringa matchata dall’espressione regolare
con un’altra stringa. La prima infatti è effettivamente
una espressione regolare, mentre la seconda è una semplice
stringa. Infine la traslazione tr/<stringa>/<stringa>/
richiede due stringhe come parametri (anche se è possibile
utilizzare la notazione col trattino: 0-7 abbrevia 01234567)
e trasla ogni carattere della prima stringa nel corrispondente
carattere della seconda).
Adesso dovrebbero essere chiari gli
esempi di prima: (a) ricerca il carattere 0 nella
sottostringa, (b) ricerca sequenze di spazi bianchi
eventualmente vuote o sequenze alfanumeriche non vuote, (c)
sostituisce sequenze di spazi, tab e newline con un singolo carattere
spazio (la ricerca e sostituzione viene ripetuta per tutta la stringa
come specificato dal suffisso g), infine (d) converte
in maiuscolo tutti i caratteri minuscoli.
Per finire questa introduzione alle
espressioni regolari occorre ricordare che esse sono un potente
strumento di analisi lessicale, grazie ai raggruppamenti che
analizzano le stringhe e assegnano alle variabili $1, $2,
..., $i. Vediamo un esempio tratto dal programma:
/^(\d\d?)\/(\w\w\w)\/(\d\d\d\d)$/
Questa espressione regolare è soddisfatta da stringhe come
“9/Lug/1968” e lascia nella variabile $1
"9", nella $2 "Lug" e nella
$3 "1968". La regola è che durante il
matching viene assegnata alle variabile $i la sottostringa
matchata dalla parte dell’espressione regolare racchiusa
nell’i-sima coppia di parentesi.
Un’espressione regolare può
essere utilizzata senza l’operatore =~ quando si applica
alla variabile di default, $_. In questo caso scrivere
semplicemente la <regexp> equivale a scrivere $_ =~
<regexp>. Esiste anche l’operatore !~ che
equivale a scrivere !(<exp>=~<regexp>).
|