Una delle caratteristiche principali del Perl è sicuramente quella di poter operare in modo estremamente flessibile sulle stringhe di caratteri. Il concetto che sta alla base di questa funzionalità è la possibilità di descrivere in modo generico un certo pattern, ossia uno schema di costruzione di una stringa, per poter effettuare dei confronti o delle sostituzioni all'interno di altre stringhe.
Ciò che ci permette di fare la maggior parte dei linguaggi di programmazione è di verificare che una certa sottostringa sia contenuta in una stringa più lunga. Il Perl, come vedremo nelle prossime pagine, ci consente di generalizzare questa operazione ricercando all'interno di una stringa non una sottostringa ben precisa, ma una generica sottostringa la cui struttura viene descritta utilizzando una certa sintassi (espressioni regolari). In altri termini possiamo dire che l'operazione di pattern matching ci permette di verificare se una stringa appartiene o meno ad un insieme di stringhe, descritto mediante una espressione regolare.
In questo contesto parlando di espressioni regolari intenderemo una espressione costruita secondo una sintassi ben precisa che ci permette di descrivere uno schema di stringa.
Senza entrare in ulteriori dettagli cerchiamo di capire questo concetto mediante un esempio elementare. Supponiamo di voler 'descrivere' tutte le stringhe composte secondo il seguente schema: un numero con un qualsiasi numero di cifre, un carattere di spaziatura o tabulazione ed una parola composta da caratteri qualsiasi. Con una espressione regolare diventa banale descrivere questa 'ricetta' di composizione di una stringa:
\d+\s.+ |
I primi due caratteri '\d' indicano la presenza di un carattere numerico (0, 1, 2, ..., 9); il carattere '+' che segue una certa sequenza indica che il carattere rappresentato può essere ripetuto una o più volte. La sequenza '\s' indica un qualsiasi carattere di spaziatura o di tabulazione. Infine il punto '.' indica un carattere qualsiasi e il simbolo '+' finale sta ad indicare che questo carattere può essere ripetuto una o più volte.
Questa espressione regolare descriverà quindi stringhe del tipo '1234 pippo', '1 ab$\%&xy', '1 2'. Le seguenti stringhe invece non risultano 'descritte' dalla precedente espressione regolare: 'a b', 'pippo', 'albero casa', '1+3=4'.
La seguente tabella descrive sinteticamente i termini che possono comporre una epressione regolare:
. | qualsiasi carattere escluso il new line ('\n') |
[a-z0-9] | qualsiasi carattere di questo insieme |
[^a-z0-9] | qualsiasi carattere esclusi quelli di questo insieme |
\d | una cifra qualsiasi; equivalente a '[0-9]' |
\D | un carattere che non sia una cifra; equivalente a '[^0-9]' |
\w | un carattere alfanumerico; equivalente a '[a-zA-Z0-9]' |
\W | un carattere non alfanumerico; equivalente a '[^a-zA-Z0-9]' |
\s | un carattere di spaziatura (spazio, tabulazione, new line, ecc.) |
\S | un carattere non di spaziatura |
\n | il carattere new line |
\r | il carattere return (ritorno carrello) |
\t | il carattere di tabulazione |
\f | form feed, carattere di avanzamento di pagina |
\b | backspace, cancellazione di un carattere a sinistra |
\0 | null, il carattere nullo |
\| | il carattere '|' |
\\ | il carattere i\' |
\* | il carattere '*' |
x? | il carattere x ripetuto 0 o 1 volta |
x* | il carattere x ripetuto 0 o più volte |
x+ | il carattere x ripetuto una o più volte |
pippo | la stringa ``pippo'' |
aa|bb|cc | la stringa 'aa' oppure la stringa 'bb' oppure la stringa 'cc' |
^ | la stringa inizia con l'espressione regolare seguente |
$ | la stringa termina con l'espressione regolare precedente |
Con il termine pattern matching si intende l'operazione di verifica se una certa stringa o una sua sottostringa corrisponde ad un certo pattern, ossia se è costruita secondo un determinato schema.
In Perl l'operatore che consente di effettuare il pattern matching è indicato da 'm/espressione regolare/''; in effetti la lettera 'm' (match) che precede la coppia di slash ('/') può essere omessa.
Per verificare se la stringa contenuta nella variabile $a (o una sua sottostringa) corrisponde al pattern descritto da una certa espressione regolare si utilizzerà la seguente istruzione, che restituisce naturalmente il valore 'vero' se il matching riesce e 'falso' altrimenti:
$a = /espressione regolare/
Supponiamo quindi di voler leggere dal canale di input standard una stringa immessa dall'utente e di voler verificare se la stringa è strutturata in un certo modo (ad esempio un numero, seguito da uno spazio e quindi da una stringa qualsiasi); il programma potrebbe essere il seguente:
#!/usr/local/bin/perl print "Inserisci una stringa: "; $a = <STDIN>; if ($a =~ /^\d+\s.+/) { print "Ok, il formato della stringa e' corretto\n"; } else { print "Il formato non e' quello desiderato\n"; } |
Supponiamo ora di voler stampare tutte le righe di un certo file che contengono una stringa inserita dall'utente. Un possibile programma per la soluzione di questo semplice problema è il seguente:
#!/usr/local/bin/perl print "Nome del file: "; $file = <STDIN>; chop($file); -e $file || die "Il file non esiste!\n\n"; -T $file || die "Il file non e' un file di testo!\n\n"; print "Stringa da cercare: "; $stringa = <STDIN>; chop($stringa); open (IN, "< $file") || die "impossibile aprire $file.\n\n"; while ($r = <IN>) { $r =~ /$stringa/ && print $r; } close(IN); |
Nell'effettuare il matching di una espressione è possibile tenere memoria di alcune componenti dell'espressione stessa. In un'espressione regolare, ogni termine racchiuso tra parentesi tonde verrà memorizzato e ci si potrà riferire ad esso all'interno della stessa espressione mediante la sequenza '\n', dove n è il numero progressivo del termine memorizzato.
Ad esempio la seguente espressione regolare 'descrive' tutte le stringhe del tipo "x = x":
/^(.+)=\1$/ |
Infatti l'espressione indica che la stringa deve iniziare ("^") con una certa stringa non nulla ("(.+)") che viene memorizzata in "\1", quindi deve essere presente il simbolo di uguaglianza ("="), e deve terminare ("$") con una stringa identica alla precedente ("\1").
Nelle istruzioni successive ad un pattern matching ci si potrà riferire alle variabili "\n" mediante le variabili "speciali" $n.
L'operazione di pattern substitution consente di cercare una certa sottostringa all'interno di una stringa (effettuando il pattern matching con una espressione regolare) e di sostituire a questa sottostringa un altro insieme di caratteri. L'operatore che consente di effettuare questa operazione ha la seguente sintassi:
s/pattern di ricerca/pattern di sostituzione/
La sostituzione del primo pattern con il secondo viene effettuata una sola volta (sulla prima occorrenza del primo pattern) a meno che dopo il terzo slash non si aggiunga il carattere "g" (globally) che indica di ripetere la sostituzione su tutte le occorrenze del pattern di ricerca e non solo sulla prima.
Modifichiamo l'esempio precedente per produrre un semplice programmino che cerca una certa stringa all'interno di un file di input e genera un file di output in cui ogni occorrenza della stringa è stata sostituita da una seconda stringa inserita dall'utente.
#!/usr/local/bin/perl print "Nome del file di input: "; $file = <STDIN>; chop($file); -e $file || die "Il file non esiste!\n\n"; -T $file || die "Il file non e' un file di testo!\n\n"; print "Stringa da cercare: "; $stringa = <STDIN>; chop($stringa); print "Stringa da sostituire: "; $sost = <STDIN>; chop($sost); open (IN, "< $file") || die "Impossibile aprire $file.\n\n"; open (OUT, "> $file.new") || die "Impossibile creare $file.new\n\n"; while ($r = <IN>) { $r =~ s/$stringa/$sost/g; print OUT $r; } close(IN); close(OUT); |