| |||
| © Perl Mongers Italia. Tutti i diritti riservati. | |||
Se avete sentito parlare delle espressioni regolari, ma avete sempre
... 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 Analizziamo la wildcard Il problema è che semplice spesso litiga con flessibile e versatile. Se volessimo trovare tutti i file che
"iniziano con Regex in PerlVisto 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 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 if ($testo =~ /QUI IL CONTENUTO DELLA REGEX/) {
print("ok, compatibile\n");
}
else {
print("NO, incompatibile\n");
}
L'operatore 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 basiI 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 /./ Questo significa che 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' Come controlliamo che ci siano almeno tre caratteri fra /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: * ripete l'elemento 0 o più volte
+ ripete l'elemento 1 o più volte
? l'elemento 0 o 1 volta
{n} l'elemento esattamente n volte
{n,} l'elemento almeno n volte
{n,m} l'elemento un numero minimo di n volte e massimo di m
Dunque, la nostra wildcard "tutti i file PDF che hanno /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: " pipporicette1234abcdef.pdf-old
^^^^^^^^^^^^^^^^^^^^^
Hey! Verifica! Ma noi vogliamo i nomi che cominciano con ^ 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
/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:
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 è /^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, /^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 E adesso, la canna da pescaCon 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 ^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',
'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 #!/usr/bin/perl
2 use strict;
3 use warnings;
4 use Tk;
5 use YAPE::Regex::Explain;
6 my $mw = MainWindow->new();
7 my $top_frame = $mw->Frame();
8 $top_frame->pack(-side => 'top');
9 my $text = $mw->Scrolled(qw/Text -scrollbars e/);
10 $text->pack(-side => 'bottom', -expand => 1, -fill => 'both');
11 $top_frame->Label(-text => 'Espressione regolare:')->pack(-side => 'left');
12 my $entry = $top_frame->Entry();
13 $entry->pack(-side => 'left');
14 $top_frame->Button(
15 -text => 'Spiega',
16 -command => sub {
17 my $yape = YAPE::Regex::Explain->new($entry->get());
18 $text->selectAll();
19 $text->Insert($yape->explain());
20 $text->unselectAll();
21 }
22 )->pack(-side => 'left');
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 ( 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? | |||