Espressioni regolari in PHP

Espressioni regolari

Inauguriamo la serie di guide avanzate sul linguaggio PHP con le espressioni regolari. In questa guida vedremo cosa sono, come funzionano e come usare le espressioni regolari in modo efficiente e produttivo all'interno di un'applicazione web.

Cosa sono le espressioni regolari

Le espressioni regolari, comunemente note come "regex" o "RegExp", sono stringhe di testo appositamente formattate utilizzate per trovare corrispondenze (in inglese matches) all'interno di una stringa di testo oppure sostituire uno o più caratteri all'interno di una stringa. Sono uno degli strumenti più potenti oggi disponibili per l'elaborazione e la manipolazione efficiente del testo.

Un comune utilizzo delle espressioni regolari è nella verifica del formato di dati come email, codice fiscale, date nella compilazione di un modulo da parte dell'utente. Le regex ci consentono di controllare la correttezza dei dati inseriti oppure di trovare e sostituire una stringa all'interno di un'altra.

PHP 5.3 e le versioni successive supportano le espressioni regolari in stile Perl attraverso la famiglia di funzione preg_*. Perl (Practical Extraction and Report Language) è stato il primo linguaggio di programmazione che ha fornito un supporto integrato per le espressioni regolari, che sono parte integrante del linguaggio.

Cominciamo con una breve panoramica delle funzioni integrate in PHP comunemente utilizzate per eseguire espressioni regolari.

Funzioni

Prima di dare uno sguardo alla sintassi delle regex, elenchiamo velocemente le funzioni supportate dal linguaggio per eseguire espressioni regolari

Funzione e sintassi Descrizione
preg_match($pattern, $subject, $matches) Verifica se viene trovata almeno una corrispondenza nella stringa $input in base all'espressione regolare $pattern. Tale numero viene ritornato in $matches
preg_match_all($pattern, $subject, $matches) Ricerca tutte le espressioni regolari $pattern all'interno della stringa $subject. Le occorrenze trovate vengono ritornate in $matches
preg_replace($pattern, $replacement, $subject, $limit, $count) Sostituisce tutte le occorrenze trovate nella stringa $subject con la stringa $replacement in base all'espressione regolare $pattern. Se presente, il parametro $limit limita il numero di sostituzioni da effettuare
preg_grep($pattern, $array) Restituisce un'array formato dagli elementi dell'array $array che soddisfano i criteri impostati nel parametro $pattern.
preg_split($pattern, $subject) Suddivide la stringa $subject in sottostringhe utilizzando l'espressione regolare $pattern
preg_quote($string) Antepone il carattere di escape (\) davanti a ciascun carattere speciale di un'espressione regolare presente nella stringa $string

Nota: La funzione PHP preg_match() interrompe la ricerca dopo aver trovato la prima occorrenza, mentre preg_match_all() continua la ricerca fino alla fine della stringa e trova tutte le possibili occorrenze invece di fermarsi alla prima.

Sintassi

Per utilizzare le espressioni regolari bisogna prima imparare la sintassi, ossia una serie di lettere, numeri, trattini e caratteri speciali.

Cominciamo inanzitutto con i caratteri speciali, a cui viene assegnato un significato speciale all'interno di un'espressione regolare, mentre tutti gli altri caratteri (es. a, b, c, d, etc...) mantengono il loro significato letterale.

La seguente tabella elenca i caratteri speciali e il loro significato: 

Carattere speciale Significato
+ indica una o più occorrenze (di un carattere o di un gruppo di caratteri)
* 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 quadre, usate per contenere una classe di caratteri (es. [a-z])
^ indica l'inizio della stringa (o la negazione della stessa se posto all'interno di una classe di caratteri)
. indica qualsiasi carattere (escluso un a capo)
$ indica la fine della stringa
() le parentesi tonde, usate per contenere una sottostringa
{} le parentesi graffe, usate per indicare il numero esatto, minimo o massimo o l'intervallo di occorrenze (di un carattere o di un gruppo di caratteri)
- usato per specificare una serie di elementi o un intervallo (es. 0-9)
| indica l'operatore OR
\ effettua l'escape per i caratteri speciali

Nei paragrafi successivi vedremo tutti gli operatori per poter costruire un'espressione regolare in PHP e introdurremo alcuni esempi di utilizzo.

Classi di caratteri

Un insieme di caratteri racchiusi tra parentesi quadre sono chiamate classe di caratteri, ad es. [abc]. Una classe di caratteri corrisponde sempre a un singolo carattere da un elenco di caratteri specificati, il che significa che l'espressione [abc] vuol dire solo a, solo b o solo c.

È inoltre possibile definire classi di caratteri negati che corrispondono a qualsiasi carattere tranne quelli contenuti tra parentesi. Una classe di caratteri negata viene definita posizionando il simbolo ^ (accento circonflesso) immediatamente dopo la parentesi di apertura, es. [^abc]

Possiamo anche definire un intervallo di caratteri utilizzando il trattino (-) all'interno di una classe di caratteri, es. [0-9] ossia qualsiasi numero da 0 a 9.

Per capire meglio vediamo alcuni esempi di classi di caratteri:

Classe Significato
[abc] Uno qualunque tra i caratteri a, b o c
[^abc] Qualsiasi carattere tranne a, b o c
[a-z] Qualsiasi carattere dalla a minuscola alla z minuscola
[A-Z] Qualsiasi carattere dalla a maiuscola alla z maiuscola
[0-9] Qualsiasi numero da 0 a 9
[a-z0-9] Qualsiasi carattere dalla a minuscola alla z minuscola e qualsiasi numero da 0 a 9

Vediamo qualche esempio di utilizzo con la funzione preg_match():

<?php
$pattern = '/[0-9]/';
$subject = 'PHP 8 è stato rilasciato giorno 8 dicembre 2022';

if (preg_match($pattern, $subject, $matches)) {
    echo "Trovate $matches[0] occorrenze"; //Trovate 8 occorrenze
}

Iniziamo col dire che in PHP una regex è racchiusa tra due slash (/) che fungono da delimitatori, nel nostro esempio: /[0-9/.

Nel nostro esempio vogliamo trovare almeno un numero all'interno della stringa $subject. La funzione preg_match() ritorna 1 se viene trovata un'occorrenza, 0 se non viene trovata oppure false se fallisce.

Il parametro $matches è un array contenente il numero di occorrenze trovate e dato che la funzione ritorna il valore 1, viene stampato Trovate 8 occorrenze.

Classi di caratteri predefinite

Alcune classi di caratteri come cifre, lettere e spazi bianchi vengono utilizzate così frequentemente che esistono delle scorciatoie (o abbreviazioni) per poterle utilizzare più facilmente.

La seguente tabella elenca queste classi di caratteri predefinite:

Abbreviazione Significato
. corrisponde a qualsiasi singolo carattere eccetto il il ritorno a capo \n.
\d corrisponde a qualsiasi carattere numerico. Uguale a [0-9]
\D corrisponde a qualsiasi carattere diverso da una cifra. Uguale a [^0-9]
\s corrisponde a qualsiasi carattere di spaziatura (spazio, tabulazione, carattere di ritorno a capo). Uguale a [\t\n\r]
\S corrisponde a qualsiasi carattere diverso da spazi bianchi. Uguale a [^\t\n\r]
\w corrisponde a qualsiasi carattere alfanumerico (definito come dalla a alla z, dalla A alla Z, da 0 a 9 e l'underscore). Uguale a [a-zA-Z0-9_]
\W Corrisponde a qualsiasi carattere non alfanumerico. Uguale a [^a-zA-Z0-9_]

Vediamo qualche esempio di utilizzo:

<?php
$string = "IT 60 X 05428 11101 000000123456";
$string = preg_replace('/\s+/', '', $string);

echo $string;

In questo esempio abbiamo una stringa contenente un codice IBAN e vogliamo rimuovere tutti gli spazi tramite la funzione preg_replace().

L'espressione regolare utilizzata è composta dall'abbreviazione \s e il carattere speciale +, in breve stiamo dicendo alla funzione preg_replace() che vogliamo rimuovere più volte (+) tutte le occorrenze contenenti uno tra spazio, tabulazione o un a capo (\s)

Quantificatori di ripetizione

Supponiamo di voler trovare parole contenenti una, due o più occorrenze di una determinata lettera o numero. È qui che entrano in gioco i quantificatori di ripetizione.

Con i quantificatori possiamo specificare quante volte un carattere in un'espressione regolare deve corrispondere.

La tabella seguente elenca i vari quantificatori di ripetizione, agendo sulla lettera "a"

Quantificatore Significato
a+ Corrisponde a una o più occorrenze della lettera a
a* Corrisponde a zero o più occorrenze della lettera a
a? Corrisponde a zero o una occorrenza della lettera a
a{2} Corrisponde esattamente a due occorrenze della lettera a
a{2,4} Corrisponde ad almeno due occorrenze della lettera a, ma non più di quattro occorrenze della lettera a
a{2,} Corrisponde a due o più occorrenze della lettera a
a{,4} Corrisponde al massimo a quattro occorrenze della lettera a

Nell'esempio specifico vogliamo verificare il formato (italiano e inglese) di una data

<?php
$date1 = "20/12/2022"; //Formato italiano
$date2 = "2022-06-10"; //Formato inglese

$pattern1 = "/(\d{2})\/(\d{2})\/(\d{4})/";
$pattern2 = "/(\d{4})-(\d{2})-(\d{2})/";

if (preg_match($pattern1, $date1)) {
    echo "Il formato della data $date1 è corretto";
    echo "<br>";
}

if (preg_match($pattern2, $date2)) {
    echo "Il formato della $date2 è corretto";
}

Nell'esempio precedente, l'espressione regolare memorizzata in $pattern1 consente di controllare la data nel formato gg/mm/yyyy che è quello comunemente utilizzato in Italia.

Commentiamo l'espressione regolare:

  • le parentesi tonde () indicano una sottostringa
  • \d corrisponde a "qualsiasi numero"
  • {2} corrisponde a esattamente 2 occorrenze di \d
  • \/ vuol dire che la stringa deve proseguire con uno slash /
  • lo schema quindi si ripete un'altra volta per il mese
  • l'espressione termina con una \d{4} che corrisponde a 4 occorrenze di un qualsiasi numero a indicare l'anno della data

Chiaramente l'espressione regolare non può verificare l'esistenza della data, ma solamente il formato. Infatti se indichiamo il valore 23/13/2022 nella variabile $date1, la verifica va a buon fine nonostante la data non esista.

Ancore

Ci sono alcune situazioni è necessario trovare un'occorrenza esattamente all'inizio o alla fine di una stringa. Per fare questo possiamo usare le ancore. Questo sono l'accento circonflesso ^ che rappresenta l'inizio della stringa e il simbolo del dollaro $, che invece rappresenta la fine della stringa.

Ancora Significato
^a Corrisponde alla lettera a all'inizio di una stringa
$a Corrisponde alla lettera a alla fine di una stringa

Vediamo un esempio con la funzione preg_grep() del linguaggio:

<?php
$pattern = "/^A/";
$names = ["Agnese", "Pippo", "Andrea", "Aron", "Trisha"];
$matches = preg_grep($pattern, $names);

foreach($matches as $match){
    echo $match . "<br>"; //Stampa i nomi che iniziano con la lettera A
}

L'esigenza è quella di trovare tutti i nomi dell'array $names che iniziano con la lettera A (a grande). Per lo scopo utilizziamo l'espressione regolare composta dall'ancora ^ seguita dalla lettera "A".

Per rendere il codice più flessibile in modo tale che funzioni sia nel caso di nomi che iniziano con la a minuscola (a), sia nel caso di nomi che iniziano con la a maiuscola (A), possiamo scrivere

<?php
$pattern = "/^a/i";
$names = ["agnese", "Pippo", "Andrea", "aron", "trisha"];
$matches = preg_grep($pattern, $names);

foreach($matches as $match){
    echo ucfirst($match) . "<br>";
}

Con il codice appena visto otteniamo lo stesso risultato del primo esempio utilizzando il modificatore i (che vedremo tra poco) dopo l'espressione regolare, mentre la funzione ucfirst() dentro il ciclo foreach rende maiuscola la prima lettera di ciascun nome.

Modificatori

I modificatori consentono di espandere o restringere la dimensione del testo che l'espressione regolare deve verificare. Vengono posizionati direttamente dopo l'espressione regolare. Se ad esempio vogliamo trovare una corrispondenza senza distinzione tra maiuscole e minuscole, è possibile utilizzare il modificatore i in questo modo: /pattern/i.

La tabella seguente elenca alcuni dei modificatori più comunemente usati;

Modificatore Significato
i esegue una ricerca case-insensitive, ossia senza distinzione tra maiuscole e minuscole
m Modifica il comportamento delle ancore ^ e $, esse verranno applicate per ogni riga di testo
g esegue una ricerca "globale", ossia trova tutte le occorrenze senza fermarsi alla prima
o valuta l'espressione regolare solamente una volta
s Modifica il comportamento di ., in modo tale che corrisponda a tutti i caratteri, comprese le nuove righe.
x Consente di utilizzare spazi bianchi e commenti all'interno di un'espressione regolare per maggiore chiarezza.

Risorse utili

In questa guida abbiamo imparato cosa sono le espressioni regolari e come possono esserci d'aiuto nelle nostre applicazioni PHP. Per poter creare, testare e debuggare le proprie regex è possibile utilizzare il servizio regex101 disponibile al seguente indirizzo https://regex101.com.

Guida successiva: Gestione errori in PHP

Sviluppatore PHP Freelance

Stai cercando uno sviluppatore PHP? Ho oltre 10 anni di esperienza nello sviluppo web con questo linguaggio.

Contattami adesso!