Sei sulla pagina 1di 7

Flavio Poletti

Espressioniche?
http://www.perl.it/documenti/articoli/2005/11/espressioniche.html
Se avete sentito parlare delle espressioni regolari, ma avete sempre
pensato che fossero solo concorrenti delle fave di fuca...
dichiarato che voi tutto, ma la matematica no...
sostenuto che un'accozzaglia di caratteri esprime poco, e non sicuramente regolare...
... allora forse riusciamo a farvi cambiare idea.
Cominciamo a renderci conto che probabilmente sappiamo gi usare dei lontani cugini delle
espressioni regolari: le cosiddette wildcard. Vi mai capitato di usare l'asterisco cercando un file?
Tipo: per avere tutti i file PDF, digitare qualcosa come dir *.pdf? Beh, che lo vogliate o no, vi
state gi piegando al concetto che le espressioni regolari vogliono esprimere.
Analizziamo la wildcard *.pdf. Cosa significa? Semplice: "prendi tutti quei file che hanno una
sequenza iniziale qualunque, ma che finiscono con i caratteri .pdf", nell'ordine. E se vogliamo
cercare tutti i file PDF di ricette? Beh, probabilmente qualcosa tipo ricette*.pdf potrebbe fare al
caso nostro, perch sta chiedendo: "tutti i file che cominciano con ricette, poi hanno una
sequenza qualsiasi di caratteri, ma finiscono con .pdf". Nessuno credo abbia da obiettare sulla
semplicit di questo strumento.
Il problema che semplice spesso litiga con flessibile e versatile. Se volessimo trovare tutti i file che
"iniziano con ricette, poi hanno una qualunque serie di cifre numeriche, e finiscono con .pdf"
saremmo gi nei guai. Qualcuno non ce l'ha fatta a star dentro a questa gabbia: voleva tutto.
Voleva un sistema per poter descrivere in dettaglio che tipo di struttura deve avere un testo perch
possa essere considerato "valido". E sono nate le espressioni regolari.

Regex in Perl
Visto che l'idea di stare in gabbia piace a pochi, si trovano molte varianti delle espressioni regolari,
ed ogni linguaggio o strumento (come il mitico grep) presenta le sue peculiarit. Perl per si
distingue in modo particolare: le espressioni regolari (le cosiddette regex) fanno parte del
linguaggio, non di una qualche libreria esterna, il che rende semplice inserirle nel proprio codice.
Negli esempi che faremo, assumeremo sempre di dover verificare se un dato testo compatibile
con una regex oppure no. Se il nostro testo contenuto in $testo, questa verifica viene fatta
come segue:
Perl Mongers Italia. Tutti i diritti riservati.

1 of 7

Flavio Poletti

Espressioniche?
http://www.perl.it/documenti/articoli/2005/11/espressioniche.html
if ($testo =~ /QUI IL CONTENUTO DELLA REGEX/) {
print("ok, compatibile\n");
}
else {
print("NO, incompatibile\n");
}

L'operatore =~ detto operatore di binding, per il fatto che effettua una connessione (il binding) fra
la variabile $testo e l'espressione regolare. In sostanza, pu essere letto come "applica
l'espressione regolare a destra sul testo della variabile a sinistra". Attenzione, dunque: non si tratta
n di un'assegnazione n di un confronto.
Inoltre, seguiremo la tradizione dicendo che c' un match se il testo compatibile con la regex.
A questo punto, allacciamo le cinture e partiamo!

Le basi
I concetti base per iniziare a giocare con le regex sono fondamentalmente due: gli elementi ed i
quantificatori. Gli elementi rappresentano grossolanamente una parte di testo. I quantificatori
esprimono invece quante volte ci si aspetta che il dato elemento possa essere ripetuto.
Gli elementi pi semplici sono i caratteri alfanumerici stessi. Essi semplicemente rappresentano s
stessi; l'espressione regolare
/a/

indica che $testo deve contenere almeno una a. Alcuni caratteri, per, non rappresentano s
stessi, come il punto:
/./

Questo significa che $testo deve contenere almeno un carattere. Qualunque carattere. Va bene,
quasi qualunque carattere, ma per ora va bene cos.
A questo punto siamo gi in grado di scrivere alcune espressioni regolari:
/ricette.pdf/

==matcha==>

'ricette-pdf', 'ricettexpdf', ...

Ebbene s, non si parla sempre di file! Se vogliamo un punto vero e proprio dobbiamo segnalarlo
con un backslash:
/ricette\.pdf/

==matcha==> 'ricette.pdf' ma NON 'ricette-pdf'

Perl Mongers Italia. Tutti i diritti riservati.

2 of 7

Flavio Poletti

Espressioniche?
http://www.perl.it/documenti/articoli/2005/11/espressioniche.html
Come controlliamo che ci siano almeno tre caratteri fra ricette e .pdf? Un sistema ripetere:
/ricette...\.pdf/

==matcha==> 'ricetteABC.pdf' e 'ricette....pdf'

A parte che per 50 caratteri invece di 3 diventa difficile, come facciamo, ad esempio, a dire "una
qualunque sequenza (anche vuota) di qualunque carattere"? A questo ci pensano i quantificatori:
*
+
?
{n}
{n,}
{n,m}

ripete l'elemento 0 o pi volte


ripete l'elemento 1 o pi volte
l'elemento 0 o 1 volta
l'elemento esattamente n volte
l'elemento almeno n volte
l'elemento un numero minimo di n volte e massimo di m

Dunque, la nostra wildcard "tutti i file PDF che hanno ricette e .pdf" si scrive cos (per il
momento!)
/ricette.*\.pdf/

C'e ancora qualcosa che non va, per. Le regex sono flessibili, abbiamo detto; questo si traduce nel
fatto che assumono il meno possibile su quello che volete realmente fare. La regex sopra dice:
"$testo deve contenere la sequenza ricette, seguita da una sequenza qualsiasi di caratteri,
seguita da .pdf". N pi, n meno. Che ne dite allora di questo testo?
pipporicette1234abcdef.pdf-old
^^^^^^^^^^^^^^^^^^^^^

Hey! Verifica! Ma noi vogliamo i nomi che cominciano con ricette, e finiscono con .pdf. Beh, va
detto utilizzando dei marcatori speciali:
^
$

marca l'inizio della riga


marca la fine della riga

Questi, in realt, sono degli elementi a tutti gli effetti, solo che la loro lunghezza nulla (in termini
di numero di caratteri su cui verificato il match) e la loro posizione fissata. La regex giusta
allora:
/^ricette.*\.pdf$/

Gi sento il brusio. Qualcuno sta borbottando: "non mi sembra che siamo andati molto oltre le
wildcard!". Hey, ci stiamo solo scaldando, quindi calma, eh? Avevamo detto che volevamo qualcosa
tipo "inizia con ricette, prosegue con caratteri numerici, finisce con .pdf". Mi sembra che siamo
a buon punto, manca solo la roba dei numeri. Qui il problema che "una sequenza di caratteri
numerici" vuol dire "una sequenza di caratteri 1, oppure 2, oppure 3, ecc.". Come si dice "oppure"?
Semplice, con la barretta verticale:
Perl Mongers Italia. Tutti i diritti riservati.

3 of 7

Flavio Poletti

Espressioniche?
http://www.perl.it/documenti/articoli/2005/11/espressioniche.html
/0|1|2|3|4|5|6|7|8|9/

Qui si pone un problema, per. Se scriviamo:


/^ricette0|1|2|3|4|5|6|7|8|9*\.pdf$/

otteniamo:
inizia con ricette0
oppure contiene 1
oppure contiene 2
...
oppure finisce con una sequenza qualsiasi di 9, seguita da .pdf
Oooops. Stiamo facendo l'OR sugli elementi sbagliati! Che faremmo di solito? Useremmo le
parentesi. E qui? Pure:
/^ricette(0|1|2|3|4|5|6|7|8|9)*\.pdf$/

Funziona? S, ma brutto. brutto per due motivi: perch brutto (ok, sono ricorsivo), e perch si
rischia di sbagliare. Perl di solito mette a disposizione pi frecce per colpire la stessa mela; nel
nostro caso, la prima \d, che vuol dire semplicemente "qualunque carattere numerico":
/^ricette\d*\.pdf$/

Chiaro, conciso, e comincia a sembrare un'accozzaglia. Ma che potenza! Riecco il brusio. chiaro
che il tizio in seconda fila (che esiste davvero!) vuole fare polemica: "e se volessi solo cifre
numeriche da 6 a 9?". Beh, a questo ci penso con la seconda freccia: le classi di caratteri. Un
esempio prima di tutto:
/^ricette[6789]*\.pdf$/

Quello che racchiudo fra parentesi quadre un insieme di caratteri, ognuno dei quali pu andare
bene. In pratica, [6789] vuol dire "un qualsiasi carattere fra 6, 7, 8 e 9". Visto che sono pigro, e
che soprattutto potrei dimenticare qualcosa, meglio usare un intervallo:
/^ricette[6-9]*\.pdf$/

Nelle classi posso mettere anche gli altri caratteri, ovviamente; quindi:
/^ricette[6-9a-fp-z]*\.pdf$/

vuol dire quello che immaginate: "inizia con ricette, seguito da una qualsiasi sequenza di cifre
fra 6 e 9, o di lettere fra a e f, o di lettere fra p e z, seguita dalla stringa di chiusura .pdf". Spero
Perl Mongers Italia. Tutti i diritti riservati.

4 of 7

Flavio Poletti

Espressioniche?
http://www.perl.it/documenti/articoli/2005/11/espressioniche.html
che il tizio della seconda sia contento ora.

E adesso, la canna da pesca


Con tutta questa tiritera non abbiamo visto nemmeno una riga di codice utile. Senza contare che
abbiamo solo scalfito la superficie! Per io sono pigro, voi pure, per cui vi do una bella canna da
pesca, cos da mangiare ve lo trovate da soli.
Quell'impareggiabile archivio che CPAN contiene un modulo preziosissimo per chi si affaccia nel
mondo delle regex: YAPE::Regex::Explain (YAPE sta per Yet Another Parser/Extractor, ossia Ancora
un altro parser/estrattore di testo). Voi gli date una regex, lui vi dice cosa fa. L'uso piuttosto
semplice:
#!/usr/bin/perl
use strict;
# Sempre!
use warnings; # Sempre in debug!
use YAPE::Regex::Explain;
$|++; # Disabilita buffering
print("Regex: ");
while (<>) {
chomp;
# Elimina il carattere a-capo
last if /quit/;
# Esci se richiesto
print(YAPE::Regex::Explain->new($_)->explain());
print("Regex: ");
}

Il programma effettua un ciclo sull'input, plausibilmente da tastiera. Voi inserite una riga: se
contiene la parola quit esce dal ciclo, altrimenti considera quello che avete immesso come una
regex, e vi spiega cosa vuol dire. Ad esempio, immettendo una nostra vecchia conoscenza:
^ricette[6-9a-fp-z]*\.pdf$

otteniamo:
NODE
EXPLANATION
---------------------------------------------------------------------(?-imsx:
group, but do not capture (case-sensitive)
(with ^ and $ matching normally) (with . not
matching \n) (matching whitespace and #
normally):
---------------------------------------------------------------------^
the beginning of the string
---------------------------------------------------------------------ricette
'ricette'
---------------------------------------------------------------------[6-9a-fp-z]*
any character of: '6' to '9', 'a' to 'f',
Perl Mongers Italia. Tutti i diritti riservati.

5 of 7

Flavio Poletti

Espressioniche?
http://www.perl.it/documenti/articoli/2005/11/espressioniche.html
'p' to 'z' (0 or more times (matching the
most amount possible))
---------------------------------------------------------------------\.
'.'
---------------------------------------------------------------------pdf
'pdf'
---------------------------------------------------------------------$
before an optional \n, and the end of the
string
---------------------------------------------------------------------)
end of grouping
----------------------------------------------------------------------

Il raggruppamento pi esterno pu essere ignorato, viene sempre aggiunto da Y::R::E per motivi
suoi. Per il tale in seconda fila che si agita: s, s, sono spiegati nel manuale. Tutto il resto della
stampata corrisponde a quanto abbiamo gi spiegato sopra.

E per chi non ama la console?


Il tizio in seconda fila oggi non d tregua (sai che novit...): non gli piacciono gli strumenti a riga di
comando! Attenzione, qui chiamiamo la cavalleria:
1
2
3
4
5

#!/usr/bin/perl
use strict;
use warnings;
use Tk;
use YAPE::Regex::Explain;

my $mw = MainWindow->new();

7
8

my $top_frame = $mw->Frame();
$top_frame->pack(-side => 'top');

9
10

my $text = $mw->Scrolled(qw/Text -scrollbars e/);


$text->pack(-side => 'bottom', -expand => 1, -fill => 'both');

11

$top_frame->Label(-text => 'Espressione regolare:')->pack(-side => 'left');

12
13

my $entry = $top_frame->Entry();
$entry->pack(-side => 'left');

14
15
16
17
18
19
20
21
22

$top_frame->Button(
-text
=> 'Spiega',
-command => sub {
my $yape = YAPE::Regex::Explain->new($entry->get());
$text->selectAll();
$text->Insert($yape->explain());
$text->unselectAll();
}
)->pack(-side => 'left');

Perl Mongers Italia. Tutti i diritti riservati.

6 of 7

Flavio Poletti

Espressioniche?
http://www.perl.it/documenti/articoli/2005/11/espressioniche.html
23

MainLoop();

Anche se non proprio in tema, diciamo cosa fa! Viene creato l'oggetto che rappresenta la finestra
principale (riga 6); tale finestra divisa in due parti, una superiore ($top_frame) ed una inferiore
($text). La prima contiene un'etichetta di testo ("Espressione regolare"), un campo per
l'immissione ed il bottone per chiedere la spiegazione (riga 14). La seconda dove verr stampata
la spiegazione della Regex. Alla pressione del tasto "Spiega", viene chiamata la sub associata al
parametro -command, che cancella il contenuto di $text e vi immette la spiegazione data da
Y::R::E.
L'eliminazione dei numeri di riga lasciata come esercizio :)

Per fare un grosso buco con il trapano...


... consigliabile cominciare con una punta piccola e fare un invito. Questo esattamente lo scopo
di questo articoletto: invitarvi ad entrare nel mondo delle regex, dal quale non uscirete pi dopo
averne appreso i concetti basilari e le potenzialit.
Il materiale di riferimento piuttosto copioso. Nel manuale di Perl troviamo il riferimento principe perlre, in inglese - accanto a dissertazioni introduttive pi complete di questa, come ad esempio
perlrequick, in italiano, e perlretut, in inglese. Volontari per le traduzioni?
Perl Mongers Italia. Tutti i diritti riservati.

7 of 7

Potrebbero piacerti anche