Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

Espressioni regolari: pattern, uso ed esempi

Principi base sul funzionamento delle espressioni regolari
Principi base sul funzionamento delle espressioni regolari
Link copiato negli appunti

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 e utile.

Espressione regolare sul controllo dei decimali

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}$

Classi di espressioni regolari in PHP

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).

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 cosiddetti "metacaratteri", ossia dei caratteri speciali, nel senso che, all'interno di un'espressione regolare, assumono un preciso significato. Questi caratteri sono:

Metacarattere Descrizione
. indica qualsiasi carattere (escluso un accapo)
* indica zero o più occorrenze (di un carattere o di un gruppo di caratteri)
? 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)
+ indica una o più occorrenze (di un carattere o di un gruppo di caratteri)
^ indica l'inizio della stringa (o, se all'interno di una classe di caratteri, la negazione della stessa)
$ indica la fine della stringa
| 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 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 sotto-stringa 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 accapo, 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 ?)$

Abbreviazioni delle Espressioni regolari

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 [ tnr]
  • S equivale a [^ tnr]

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 ( trn)
  • [[:upper:]] indica le lettere maiuscole
  • [[:lower:]] indica le lettere minuscole
  • [[:punct:]] indica i caratteri di punteggiatura
  • [[:xdigit:]] indica i valori esadecimali

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 :

  • 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 <title> 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:

<?php
$file_remoto = "http://www.sito.it/index.htm";
$sorgente_file_remoto = implode("", file($file_remoto));
// UTILIZZIAMO eregi() perchè il tag potrebbe essere <title> o <TITLE>
if(eregi("<title>(.+)</title>", $sorgente_file_remoto, $regs)) {
echo "Il titolo della pagina $file_remoto è " . $regs[1];
} else {
echo "Titolo non trovato";
}
?>

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 <title> 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 <title>Titolo della pagina</title>).

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, � 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:

<?php
$mail = "mail@sito.it";
echo eregi_replace("^([a-z0-9._-]+)(@[a-z0-9.-_]+)(.{1}[a-z]{2,6})$", "<a href="mailto:123">123</a>", $mail);
?>

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(), 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:

<?php
// data in formato italiano
$data = "09-11-2002";
if(ereg("([/-])+", $data, $regs))
{
$separatore = $regs[1];
}
$split_data = split("[/-]", $data);
// data in formato inglese
echo $split_data[1] . $separatore . $split_data[0] . $separatore . $split_data[2];
?>

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:

<?php
function is_email($email)
{
if(eregi("^([a-z0-9_.-])+@(([a-z0-9_-])+.)+[a-z]{2,6}$", trim($email)))
return 1;
else
return 0;
}
// esempio di utilizzo, si suppone che $_POST['Email']
// contenga l'input dell'utente
if(is_email($_POST['Email']))
{
echo "Email corretta";
} else {
echo "Email non corretta";
}
?>

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:

<?php
function CodeUrl($link)
{
$Search = "^(https?|ftp)://([^<>[:space:]]+)$";
$Replace = "<a href="\1://\2" target="_blank">\1://\2</a>";
return eregi_replace($Search, $Replace, $link);
}
// esempio di utilizzo
echo CodeUrl("http://www.html.it");
?>

Ti consigliamo anche