Vai al contenuto

Regex: Espressioni regolari in PHP, parte 2

Nel precedente articolo abbiamo iniziato a parlare di espressioni regolari. In questo approfondiremo il discorso sotto l’aspetto più tecnico. Il linguaggio PHP ha al suo interno due diversi parser per lavorare con le espressioni regolari. Uno chiamato POSIX e l’altro PCRE, di cui parleremo. Le funzioni con il prefisso ereg_ riguardano il motore POSIX (che dalla versione 5.3 il POSIX è deprecato), mentre le funzioni con il prefisso preg_ riguardano il motore PCRE. Potete leggere la lista delle funzioni nella documentazione ufficiale PHP.

Sintassi di base

Per utilizzare le espressioni regolari prima bisogna imparare la sintassi. Questa sintassi consiste in una serie di lettere, numeri, punti, trattini e simboli speciali, che possiamo raggruppare utilizzando le parentesi.

In PHP ogni pattern di una regex viene definito come una stringa utilizzando il formato Perl. In Perl, un pattern è scritto tra due slash, come ad esempio: /ciao/. In PHP bisogna utilizzare le virgolette o gli apostrofi, quindi: ‘/ciao/’. Nella costruzione del pattern ci sono quindi una serie di regole da seguire, prima fra tutte quelle che riguardano i cosiddetti metacaratteri e quantificatori, ossia i caratteri speciali che, all’interno di un’espressione regolare, assumono un preciso significato. Diamo uno sguardo agli operatori di base per costruire delle espressioni regolari:

Operatore Tipo Descrizione
^ Metacarattere indica l’inizio di un pattern, anche se in alcuni casi può essere omesso. Se all’interno di una classe di caratteri, rappresenta la negazione della stessa
$ Metacarattere indica la fine di un pattern
. Metacarattere corrisponde a qualsiasi singolo carattere
| Metacarattere operatore booleano OR
Metacarattere usato per specificare una serie di elementi o un intervallo (esempio: A-Z)
() Metacarattere identificano dei gruppi di caratteri o una sottostringa contenuta nella stringa (quindi una porzione della stringa)
[] Metacarattere identificano intervalli e classi di caratteri
\ Metacarattere indica il carattere di escape dei caratteri speciali
.
? Quantificatore indica zero o una occorrenza (di un carattere o di un gruppo di caratteri)
+ Quantificatore indica una o più occorrenze (di un carattere o di un gruppo di caratteri)
* Quantificatore indica zero o più occorrenze (di un carattere o di un gruppo di caratteri)
{min, max} Quantificatore indicano il numero esatto, o minimo, o massimo, o l’intervallo di occorrenze (di un carattere o di un gruppo di caratteri) all’interno di una stringa. Ad esempio:

  • {4} esattamente 4 volte;
  • {4,} minimo 4 volte;
  • {,4} massimo 4 volte;
  • {1,4} da 1 a 4 volte;
.
\d Abbreviazione corrisponde a qualsiasi cifra singola,
\D Abbreviazione corrisponde a qualsiasi carattere singolo
\w Abbreviazione corrisponde a qualsiasi carattere alfanumerico compreso underscore (_)
\W Abbreviazione corrisponde a qualsiasi carattere non alfanumerico escluso underscore (_)
\s Abbreviazione corrisponde al carattere di spaziatura
\S Abbreviazione corrisponde a qualsiasi carattere che non sia di spaziatura

Per capire meglio come vengono utilizzati questi operatori, diamo uno sguardo ad alcuni esempi:

Esempio Descrizione
‘/hello/’ corrisponde alla parola ciao
‘/^hello/’ corrisponde a tutte le stringhe con hello all’inizio della stringa. Possibili corrispondenze sono hello o helloworld, ma non worldhello
‘/hello$/’ corrisponde a tutte le stringhe con hello alla fine della stringa.
‘/he.o/’ corrisponde a tutte le stringhe con he, un carattere qualsiasi, e la o finale. Quindi possibili corrispondenze sono helo, heyo, hebo ma non hello (con due L)
‘/he?llo/’ Esso sarà soddisfatta llo o hello
‘/hello+/’ corrisponde alla stringa hello o più occorrenze della stringa, quindi ad esempio hello o hellohello
‘/he*llo/’ corrisponde alle stringhe llo, hello hehello , ma non hellooo
‘/hello|world/’ corrisponde alle stringhe hello oppure world
‘/(A-Z)/’ in questo modo, corrisponde alla sequenza di caratteri da A a Z (maiuscoli). Quindi A,B,C,D,…,Z
‘/[abc]/’ rappresenta una classe e corrisponde alle stringhe che contengono almeno uno dei caratteri specificati tra [], senza contare l’ordine in cui sono stati scritti
‘/[A-Z]/’ corrisponde alle stringhe che contengono almeno uno dei caratteri da A a Z (minuscoli)
‘/abc{1}/’ in questo caso le parentesi graffe si applicano all’ultimo carattere. Corrisponde alla stringa abc con solo un’occorrenza di c alla fine. Esempio: abc, ma non abcc
‘/abc{1,}/’ corrisponde alle stringhe che iniziano per ab e finiscono per uno o più occorrenze di c. Ad esempio, corrisponde a abc o abcc
‘/abc{2,4}/’ corrisponde alle stringhe che iniziano per ab e finiscono con occorrenze di c (tra due e quattro) dopo i caratteri ab . Ad esempio, abccabccc o abcccc , ma non abc.
‘/[0-9]/’ corrisponde alle stringhe che contengono uno qualsiasi dei numeri compresi nell’intervallo;

Terminologia

Classe di caratteri

La classe viene dopo la singola corrispondenza letterale. Si tratta di una sequenza di caratteri che viene specificata tra le parentesi quadre, in cui ciascun elemento presente nella classe può essere un’occorrenza in una stringa o meno. Possiamo quindi dire che [0123] è una classe valida per la stringa “1900”, poichè è presente il valore 1 definito nella classe. Una classe di caratteri permette anche di specificare un intervallo.

Gruppo

Le parentesi tonde, invece, fanno riferimento ad un gruppo, o una parte di stringa, che viene assunta per il suo esatto valore letterale. Quindi possiamo dire che (ia) è un gruppo valido per la stringa “ciao”.

Qualche esempio in PHP

Poniamo di definire la seguente stringa:

$a = 'Ho comprato 2 mele al mercato';

e che il numero di mele possa variare in modo indefinito. Vogliamo che il matching abbia esito positivo solo se le rose comprate sono 2 oppure 3. La soluzione al problema potrebbe essere quella di usare un gruppo con il metacarattere |:

$p = '/^Ho comprato (2|3) mele al mercato$/';

É tuttavia possibile ricorrere alla classi di caratteri in questo modo:

$p = '/^Ho comprato [23] mele al mercato$/';

La parte tra parentesi quadre è la classe, ed è costituita dai vari caratteri che dobbiamo considerare validi ai fini del matching, in questo caso 2 e 3. Una classe di caratteri permette anche di specificare un intervallo. Se ad esempio le rose devono essere un numero variabile da tre a otto possiamo utilizzare una qualsiasi delle seguenti tre soluzioni:

// Senza classe di caratteri
$p = '/^Ho comprato (3|4|5|6|7|8) rose nere$/';

// Con classe di caratteri senza intervallo
$p = '/^Ho comprato [345678] rose nere$/';

// Con classe di caratteri ed intervallo
$p = '/^Ho comprato [3-8] rose nere$/';

In PHP abbiamo un totale di nove funzioni PCRE che possiamo utilizzare con le regex. Ecco l’elenco:

  • preg_filter – Esegue una ricerca di espressioni regolari e sostituzione
  • preg_grep – Restituisce una matrice degli elementi riconosciuti tramite le espressioni regolari
  • preg_last_error – Restituisce il codice errore dell’ultima funzione eseguita
  • preg_match – Esegue un riconoscimento con espressioni regolari
  • preg_match_all – Esegue un riconoscimento globale con le espressioni regolari
  • preg_quote – Inserisce il carattere di escape nei caratteri delle espressioni regolari
  • preg_replace – Esegue una ricerca ed una sostituzione con le espressioni regolari
  • preg_replace_callback – Esegue ricerche e sostituzioni con espressioni regolari usando il callback
  • preg_split – Suddivide una stringa tramite le espressioni regolari

Di certo le funzioni più utilizzate sono preg_match() e preg_replace(). La funzione preg_match_all() è analoga alla funzione preg_match() con la differenza che quest’ultima si ferma alla prima occorrenza, mentre preg_match_all() effettua il confronto del pattern in modo globale, cioè sino alla fine della stringa passata come secondo parametro.

// la funzione preg_match_all($pattern, $stringa, $array)
// restituisce un intero con le occorrenze trovate in $stringa del $pattern
// specificato e memorizza in $array un array delle occorrenze trovate

// trova tutti i caratteri
$stringa = "hello world";
$i = preg_match_all('/./', $stringa, $arr);
echo("$i<br>");
print_r($arr);
echo("<br /><br />");

// trova solamente la lettera "h"
$stringa = "hello world";
preg_match_all('/^./', $stringa , $arr);
print_r($arr);
echo("<br /><br />");

// trova solamente la lettera "d"
$stringa = "hello world";
preg_match_all('/.$/', $stringa , $arr);
print_r($arr);
echo("<br /><br />");

// trova solo la e,o,u
$stringa = "hello world";
preg_match_all('/a|i|u|o|e/', $stringa, $arr);
print_r($arr);
echo("<br /><br />");

//************ USO DI QUANTIFICATORI ************//

// Trova "i" di "cammin",
// "di" di "di"
// e "i" di "vita"
$stringa = "Nel mezzo del cammin di nostra vita";
preg_match_all('/d*i/', $stringa, $arr);
print_r($arr);
echo("<br /><br />");

// Trova esattamente "di" di "di"
$stringa = "Nel mezzo del cammin di nostra vita";
preg_match_all('/d+i/', $stringa, $arr);
print_r($arr);
echo("<br /><br />");

// Trova "i" di "cammin",
// "di" di "di"
// e "i" di "vita"
$stringa = "Nel mezzo del cammin di nostra vita";
preg_match_all('/d?i/', $stringa, $arr);
print_r($arr);
echo("<br /><br />");