espressioni regolari

Materie:Appunti
Categoria:Informatica

Voto:

2.5 (2)
Download:285
Data:13.01.2006
Numero di pagine:11
Formato di file:.html (Html Document)
Download   Anteprima
espressioni-regolari_1.zip (Dimensione: 16.63 Kb)
trucheck.it_espressioni-regolari.html     6.06 Kb
trucheck.it_espressioni-regolari.doc     72 Kb
readme.txt     59 Bytes


Testo

Introduzione alle espressioni regolari
In questo articolo tratteremo le Espressioni Regolari, ma desidero chiarire preliminarmente che lo farò alla maniera di un programmatore PHP, con tutti i limiti che questo comporta. Confido quindi nella clemenza degli appassionati ed esperti di PERL che potrebbero avere da ridire su questo scritto.
Le Espressioni Regolari servono per trovare corrispondenze di modelli (pattern) su stringhe e costituiscono uno strumento tanto difficile ed ostico (soprattutto all'inizio) quanto potente ed utile.
Un primissimo esempio, per aiutare a capire di cosa stiamo discutendo chi non ne avesse mai sentito parlare, potrebbe essere quello di un campo di un modulo destinato a contenere un importo in Euro: come fare a verificare che quanto digitato dall'utente sia corretto ? Se il contenuto del field dovrà essere inserito, ad esempio, in un campo di un Database (MySQL) di tipo DECIMAL(10,2), qualora non venissero rispettate queste caratteristiche, il valore verrebbe corrotto al momento dell'inserimento e quindi torniamo al problema iniziale: come fare a verificare la correttezza formale dell'input dell'utente ? Chiaro che non possiamo fare riferimento su funzioni PHP tipo is_numeric(), occorre invece costruire un modello, la cui corrispondenza con il contenuto del campo del modulo andrà poi verificata.
Per la cronaca il modello del nostro esempio potrebbe assomigliare a questo (non spaventatevi, alla fine della lettura, vi risulterà chiarissimo):
^[0-9]{1,10}\.[0-9]{2}$
PHP conosce due classi di espressioni regolari, le POSIX e quelle compatibili con PERL. Le differenze riguardano essenzialmente la sintassi, ma è un argomento avanzato di cui non ci occuperemo, e in ogni caso qui verranno trattate soltanto le espressioni regolari del primo tipo (con le relative funzioni).

I metacaratteri
Il modello più semplice è ovviamente quello costituito da una parola o, più in generale, da una sequenza di caratteri. È chiaro che in questo caso non è conveniente usare una delle funzioni per le espressioni regolari perchè queste sono molto "dispendiose" in termini di risorse impiegate, per cui il loro utilizzo dovrebbe essere limitato ai casi in cui non sia possibile utilizzare un'altra funzione (ad esempio, strpos(), o str_replace()).
Nella costruzione del modello, la cui corrispondenza andrà verificata, ci sono naturalmente una serie di regole da seguire, prima fra tutte quella che riguarda i cosìddetti "metacaratteri", ossia dei caratteri speciali, nel senso che, all'interno di un'espressione regolare, assumono un preciso significato.
Questi caratteri sono:
. che indica qualsiasi carattere (escluso un acapo)
* che indica zero o più occorrenze (di un carattere o di un gruppo di caratteri)
? che indica zero o una occorrenza (di un carattere o di un gruppo di caratteri)
{} le parentesi graffe, che indicano il numero esatto, o minimo, o massimo, o l'intervallo di occorrenze (di un carattere o di un gruppo di caratteri)
+ che indica una o più occorrenze (di un carattere o di un gruppo di caratteri)
^ che indica l'inizio della stringa (o, se all'interno di una classe di caratteri, la negazione della stessa)
$ che indica la fine della stringa
| che indica l'operatore OR
\ il carattere di escape dei caratteri speciali
() le parentesi tonde, destinate a contenere una sottostringa
[] le parentesi quadre, destinate a contenere una 'classe' di caratteri
Caratteristica comune a tutti questi metacaratteri è che se vogliono essere usati per il loro "valore letterale" (e non per quello speciale), devono essere preceduti da un backslash (\), per cui, ad esempio, l'esistenza di un punto interrogativo all'interno di una stringa, potrebbe essere accertata con questa espressione: \?.
Vediamo adesso di esaminare più nel dettaglio i metacaratteri, cominciando dalle varie parentesi. Le parentesi quadre [], come si è accennato, racchiudono una "classe di caratteri". Questo vuol dire che il modello può o deve contenere alcuni o tutti i caratteri in esse contenute; chiariamo il concetto con degli esempi:
[abc]
questo modello è soddisfatto quando viene trovata una delle lettere, senza tener conto dell'ordine in cui sono presenti;
[a-z]
in questo modello è presente un intervallo di caratteri (notare il segno -, sta per "dalla a alla z"), esso è soddisfatto quando viene trovato uno qualsiasi dei caratteri compresi nell'intervallo;
[0-9]
in questo modello è presente invece un intervallo di numeri, esso è soddisfatto quando viene trovato uno qualsiasi dei numeri compresi nell'intervallo;
[a-z0-9\?]
questo modello è leggermente più complesso, ma dovrebbe essere di facile comprensione. La corrispondenza viene trovata quando la stringa contiene una lettera (minuscola in questo caso), un numero o il carattere ? (notate il segno \ prima di ?, perchè il punto interrogativo è un carattere speciale, che qui però assumiamo per il suo valore letterale);
[^a-z]
questo modello è soddisfatto quando viene trovato un qualsiasi carattere che non sia una lettera minuscola (notate il segno ^ che all'interno della classe, la nega);
Naturalmente una classe di caratteri può essere seguita (e normalmente lo è) da uno dei metacaratteri che indicano il numero di volte in cui uno dei caratteri in essa contenuti, deve essere presente, riprendendo l'ultimo modello:
[a-z0-9\?]?
i caratteri contenuti nella classe devono essere presenti zero o una volta;
[a-z0-9\?]*
i caratteri contenuti nella classe devono essere presenti zero o più volte;
[a-z0-9\?]{3}
i caratteri contenuti nella classe devonoo essere presenti esattamente tre volte;
[a-z0-9\?]{1,3}
i caratteri contenuti nella classe devono essere presenti da una a tre volte;
[a-z0-9\?]{3,}
i caratteri contenuti nella classe devono essere presenti minimo tre volte;
[a-z0-9\?]{,3}
i caratteri contenuti nella classe devono essere presenti massimo tre volte.

Le parentesi
Le parentesi graffe, come abbiamo già visto, indicano il numero esatto, minimo, massimo o l'intervallo di volte in cui una un'esatta sequenza o una classe di caratteri, devono essere presenti in una stringa:
{3} esattamente 3 volte;
{3,} minimo 3 volte;
{,3} massimo 3 volte;
{1,3} da 1 a 3 volte;
Le parentesi tonde, invece, fanno riferimento ad una sottostringa, o una parte di stringa se preferite, che viene assunta per il suo esatto valore letterale.
Quindi ad esempio (abc) si riferisce all'esatta sequenza di caratteri abc, a differenza, come abbiamo visto, di [abc] che si riferisce invece ad uno dei tre caratteri.
Ovviamente anche le parentesi tonde, possono essere usate con quei metacaratteri che indicano il numero di volte in cui la sottostringa deve ripetersi, per cui l'espressione (casa)? indica la presenza opzionale della parola casa (o, il che è lo stesso, che la parola deve essere presente zero o una volta).
Ma le parentesi tonde sono molto importanti anche e soprattutto perche le parti di stringa (o le espressioni) in esse contenute, possono essere "utilizzate" per vari scopi (un replace per dirne uno), ma lo vedremo più avanti quando faremo un cenno alle funzioni PHP sulle Espressioni Regolari.
Descriviamo adesso brevemente, gli altri metacaratteri. Partiamo dal punto che sta per qualsiasi carattere escluso un acapo, per cui, ad esempio, l'espressione (.)+ indica qualsiasi carattere ripetuto una o più volte (nella pratica è difficile che questo modello non trovi corrispondenza...).
Dei caratteri *,? e + abbiamo già detto in relazioni alle classi e alle sottostringhe. Il carattere | (pipe) indica l'operatore OR e consente, quindi, di presentare più alternative per un'espressione; ad esempio (bello|normale|brutto) va letta come "bello o normale o brutto" ed è quindi soddisfatta quando solo una delle tre parole viene trovata nella stringa analizzata.
Sul carattere ^ vale la pena di soffermarsi un attimo perchè, come accennato, esso assume una duplice valenza, a seconda del punto in cui si trovi all'interno dell'Espressione Regolare ed occorre quindi porre molta attenzione nel suo uso.
Se posto all'inizio del modello, tale carattere indica l'inizio esatto dello stesso: ^(ciao) indica infatti che la stringa deve iniziare con la parola ciao.
Ma l'accento circonflesso, se posto all'interno di una classe di caratteri, nega la stessa: [^0-9] indica qualsiasi carattere che non sia un numero.
Infine, il carattere $ indica la fine di una stringa, per cui se viene usato in combinazione con ^, è possibile costruire un'Espressione Regolare che indichi un modello esattamente contenuto in una stringa, ad esempio ^Ciao come stai \?$, o che indichi l'esatto inizio e l'esatta fine di una stringa, ad esempio ^(Ciao) [a-zA-Z]+ (come stai \?)$
Le Espressioni Regolari, conoscono anche, per così dire, delle abbreviazioni per ottenere ciò che si desidera, in relazione, ad esempio, alle classi di caratteri usate più di frequente. Eccone di seguito un breve schema riepilogativo:
\d equivale a [0-9]
\D equivale a [^0-9]
\w equivale a [0-9A-Za-z]
\W equivale a [^0-9A-Za-z]
\s equivale a [ \t\n\r]
\S equivale a [^ \t\n\r]
Analogamente, esistono delle classi di caratteri predefinite:
[[:alpha:]] indica qualsiasi lettera, maiuscola o minuscola
[[:digit:]] indica qualsiasi cifra
[[:space:]] indica tutti i caratteri di spazio ( \t\r\n)
[[:upper:]] indica le lettere maiuscole
[[:lower:]] indica le lettere minuscole
[[:punct:]] indica i caratteri di punteggiatura
[[:xdigit:]] indica i valori esadecimali

Le funzioni eregi ed ereg_replace
Che ci crediate o no, le poche regole appena esplicate (che non esauriscono l'argomento, comunque) sono sufficienti a permetterci di lavorare con le Espressioni Regolari e a costruire, quindi, dei validi modelli per gli scopi che ci proponiamo, mi preme solo aggiungerne una dettata dalla mia esperienza personale: prima di accingervi a costruire l'Espressione, è fondamentale che abbiate in mente l'esatto modello che volete riprodurre, le parti di cui esso si compone, in altre parole, che sappiate esattamente ciò che volete cercare delimitandone correttamente i confini.
Le funzioni che PHP ci mette a disposizione (ribadisco che faccio riferimento alle POSIX extended e non alle Perl-Compatible) per lavorare con le Reg. Exp., sono le seguenti (sul manuale ufficiale on-line, le trovate a questo link):
ereg() (e l'omologa eregi() che si differenzia solo per il fatto di essere insensibile alla differenza tra lettere maiuscole e minuscole)
ereg_replace() (e l'omologa eregi_replace() che si differenzia solo per il fatto di essere insensibile alla differenza tra lettere maiuscole e minuscole)
split() (e l'omologa spliti() che si differenzia solo per il fatto di essere insensibile alla differenza tra lettere maiuscole e minuscole)
La funzione ereg(), serve a trovare la corrispondenza di un modello (il primo argomento della funzione) all'interno di una stringa (il secondo argomento della funzione); essa si presenta, pertanto, nella seguente forma:
ereg( string epressione_regolare, string stringa [, array regs])
la funzione restituisce TRUE o FALSE a seconda se venga trovata o meno la corrispondenza (o se si verifichino degli errori).
Il terzo argomento, opzionale, se inserito, restituirà un array che conterrà tanti elementi quante sono le parti del modello poste tra parentesi tonde ritrovate nella stringa più uno che sarà costituito dall'intera stringa ritrovata, e a questo array si potrà naturalmente fare riferimento per "utilizzare" quelle parti di testo ritrovate.
Un esempio servirà a chiarire meglio quanto appena detto. Il nostro scopo è estrarre il tag da una pagina remota e dobbiamo quindi costruire un modello che riesca ad individuarlo e, successivamente, dobbiamo estrarlo e stamparlo a video.
Il codice per fare tutto ciò, potrebbe essere il seguente:

Il modello è estremamente semplice, ciò che mi preme è far rilevare come, passando alla funzione il terzo argomento (l'array $regs), siamo riusciti ad utilizzare (stampandolo a video, in questo caso) ciò che era contenuto fra parentesi tonde, cioè il tag della pagina remota che costituisce quindi il secondo elemento di $regs ($regs[1], il primo elemento $regs[0], contiene l'intera stringa ritrovata e quindi Titolo della pagina).
La funzione ereg_replace() è usata per sostituire, all'interno della stringa passata come terzo argomento, la parte di testo che corrisponda all'Espressione Regolare passata come primo argomento, con il testo passato come secondo argomento; essa si presenta, pertanto, nella seguente forma:
ereg_replace ( string espressione_regolare, string testo_sostitutivo, string stringa)
la funzione restituisce la stringa originaria (eventualmente) modificata dalla sostituzione. Analogamente a quanto accade con la funzione ereg(), anche qui è possibile "utilizzare" quelle parti di testo ritrovate nella stringa che corrispondevano alle sottostringhe dell'Espressione Regolare poste fra parentesi tonde; per far riferimento a queste parti di testo, si usa la notazione \\n, dove n è il numero progressivo (partendo da \\1, \\0 si riferisce, invece all'intera stringa) che fa riferimento alle sottostringhe comprese tra parentesi tonde e che si incrementa di pari passo (\\1 farà riferimento alla prima sottostringa, \\2 alla seconda, e così via).
Anche qui un esempio chiarirà meglio quanto detto, in questo caso ci proponiamo di rendere cliccabile, di trasformare quindi in link, un indirizzo email, e di stamparlo quindi a video, il codice per far tutto ciò, potrebbe somigliare a questo:

Assegnato alla variabile $mail l'indirizzo email, riusciamo agevolmente a trasformarlo in link, grazie all'Espressione Regolare passata come primo argomento (che non mi soffermerò a spiegare, perchè ritengo che, arrivati a questo punto, dovreste già essere in grado di capirla) ed alle opportune sostituzioni operate nel secondo parametro della funzione. Se ci fate caso, nella Reg. Exp. abbiamo 3 sottostringhe comprese fra parentesi tonde, la prima corrisponde a tutto ciò che c'è prima del segno @, (quindi a mail nel nostro caso), la seconda corrisponde a tutto ciò che è compreso tra il segno @ e il punto prima del suffisso (quindi a sito nel nostro caso), e la terza infine, corrisponde al punto più il suffisso (quindi a .it nel nostro caso).
A queste 3 sottostringhe facciamo riferimento nel testo sostitutivo con le notazioni \\1, \\2 e \\3 ed il gioco è fatto.

La funzione split
La funzione split(), infine, serve per suddividere una stringa (il secondo argomento della funzione) in più parti in base al carattere passato come primo argomento; svolge quindi lo stesso lavoro della funzione explode() ma, a differenza di questa, accetta un'Espressione Regolare come primo argomento.
È possibile quindi "splittare" la stringa non sulla base di un singolo carattere, ma di un'espressione. Importante rilevare come split() restituisca un array contenente tanti elementi quanti sono le parti della stringa risultanti dalla divisione; la funzione accetta anche un terzo parametro facoltativo (un numero intero) che, se passato, limita il numero di elementi dell'array.
Essa si presenta, quindi, nella seguente forma:
split ( string espressione_regolare, string stringa [, int limite])
Anche qui facciamo un esempio per renderci conto di come lavori split(). Supponiamo di ricevere da un modulo una data in formato italiano (giorno mese anno) e di volerla trasformare in formato inglese (mese giorno anno), con un problema però, che non sappiamo se la data verrà passata come gg/mm/aaaa o come gg-mm-aaaa.
In questo caso la funzione split() fa al caso nostro perchè possiamo passare, come primo parametro, un'Espressione Regolare che comprenda sia il carattere / che il carattere -.
Ecco il codice che possiamo utilizzare:

Poco da dire, voglio solo far rilevare come utilizziamo la funzione ereg() per tirar fuori il carattere che separa giorno, mese ed anno (memorizzato nella variabile $separatore), per riproporre lo stesso anche come separatore della data in formato inglese.
A conclusione di scritto, voglio proporvi due esempi concreti (sotto forma di funzioni) di applicazione di espressioni regolari, che potranno tornarvi utili (almeno spero) nei vostri script.
Il primo riguarda la validazione di un indirizzo email, da utilizzare, ad esempio, dopo l'invio di un modulo in cui uno dei campi debba appunto contenere un indirizzo email:

L'altro esempio, infine, ha per oggetto la "trasformazione" di un URL del tipo http://www.sito.it (ma anche in altre forme), in un link, cliccabile quindi da una pagina web:

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 ``\''
\*
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

Esempio