Oggi parlerò di espressioni regolari, spesso e volentieri molti addetti ai lavori (me compreso) utilizzano CMS che “fanno tutto loro”, e senza nemmeno preoccuparci tanto dell’aspetto tecnico in 10 minuti abbiamo messo su un portale con tanto di rewrite url o ricerche su stringhe particolarissime senza lontanamente immaginare cosa ci sia dietro. Anche per quanto riguarda la mia esperienza lavorativa quotidiana spesso sono alle prese con CMS che mi rendono la vita più facile ma tutto sommato mi “atrofizzano” il cervello facendomi spesso dimenticare ciò che fino a 4/5 anni fa per me era il “padre nostro”, quindi, questo articolo vuole essere una fonte di riepilogo e approfondimento anche per il sottoscritto.
Le espressioni regolari (chiamate anche regex) svolgono un ruolo oscuro e silenzioso che non va in nessun modo sottovalutato. Tramite le regex è possibile raggruppare, descrivere, identificare stringhe che presentino una certa regolarità al suo interno. Molti vengono a conoscenza di queste tecniche per descrivere le regole del file .htaccess all’interno di un ambiente Linux, ad esempio, per il rewrite url ma questa è solo una minima parte, le regex si possono sfruttare anche per eseguire operazioni su stringhe più o meno complesse e molti linguaggi di programmazione come ad esempio PHP offrono delle funzioni proprietarie per poter gestire l’utilizzo delle stringhe stesse tramite espressioni regolari. Chi ha studiato matematica discreta certamente non avrà bisogno della lettura di questo articolo ma per tutti gli altri, proverò a sforzarmi per cercare di farvi capire come le espressioni regolari possono svoltarci la vita all’interno di un applicazione web e non solo.
Mettiamo il caso di dover eseguire una validazione lato server di un form html che contiene i seguenti campi, come precedentemente detto è logico utilizzare le espressioni regolari se c’è una regolarità che caratterizza le stringhe, analizziamo nel dettaglio:
- Nome libro: una stringa che può contenere solo lettere dell’alfabeto (maiuscole o minuscole) più caratteri speciali come le lettere accentate;
- Editore: stesso dicasi per questo campo, può contenere lettere dell’alfabeto con le stesse regole del campo descritto precedentemente;
- Codice libro: è formato da un numero intero separato da un trattino (-);
- Recapito e-mail: stringa suddivisa in 3 parti: ammetterà caratteri alfanumerici e speciali.
Alcune piccole nozioni teoriche prima di iniziare la pratica: I metacaratteri
Prima di andare a vedere in pratica come descrivere le regole di validazione di campi partiamo dalle basi.
Le classi
Partiamo dal principio, all’interno delle due parentesi quadre è possibile descrivere una o più occorrenze di caratteri presenti. Le occorrenze descritte nelle parentesi quadre vengono dette “classi“. Possiamo quindi dire che [0123] è la classe che rappresenta la singola occorrenza di uno tra i numeri 0,1,2 e 3 potendo verificare la presenza di uno di essi in una stringa.
[ ]
Il range
Abbiamo appena visto che all’interno di una classe possiamo descrivere le occorrenze di una serie di caratteri, ad esempio [0123] indica che all’interno della stringa bisogna verificare la presenza di uno di questi quattro caratteri. Il range, invece, permette di raggruppare un set di caratteri consecutivi, per esempio:
- [a-z] individua il set di tutte le lettere minuscole che vanno dalla a alla z;
- [A-Z] stessa cosa ma per le maiuscole;
- [0-9] tutti i numeri che vanno da 0 a 9;
- [g-t] nulla mi vieta di individuare un range di caratteri che va dalla g alla t piuttosto che dal 3 al 9 e così via.
Analogamente, se abbiamo bisogno di verificare una stringa all’interno della quale ci devono essere più occorrenze nulla ci vieta di operare in questo modo:
- [a-zA-Z0-9] in questo caso sto operando su una stringa che ha al suo interno un range di caratteri alfanumerici, maiuscoli e minuscoli;
- [a-zA-Z] sto operando all’interno di una stringa che contiene solo caratteri alfabetici maiuscoli o minuscoli.
Altre operazioni sulle classi possono essere le seguenti:
- [a-zA-Zàèìòù] sto operando all’interno di una stringa che contiene oltre ai caratteri alfabetici maiuscoli e minuscoli anche caratteri speciali come: à è ì ò ù.
Altri operatori
L’operatore * (asterisco) verifica se una stringa all’interno di una classe classe si ripete ZERO o più volte.
Esempio: [a-zA-Z]*
L’operatore + (più), invece, verifica se una stringa all’interno di una classe si ripete UNA o più volte.
Esempio: [a-zA-Z ]+
Gli operatori { } (parentesi graffe), indicano le ripetizioni consecutive di una classe ed accetta solo valori numerici al suo interno, esempio {1} individua tutte le ripetizioni di 1 carattere nella classe mentre con un range del tipo {2,28} individua almeno 2 e massimo 28 ripetizioni di caratteri all’interno della classe.
Esempio 1: [0-9]{5} individua tutti i numeri da 0 a 9 composti da 5 cirfre esatte.
Esempio 2: [0-9]{5, 10} individua tutti i numeri da 0 a 9 composti da 5 almeno e massimo 10 cifre.
Operatore \ (slash), questo carattere spesso in programmazione viene utilizzato per eseguire l’escape di una stringa, infatti, se è anteposto ad un carattere di un operatore fa in modo di non essere considerato tale mentre se anteposto ad una lettera essa diventa costante. Con più chiarezza un esempio pratico sull’operatore range (-), se descritto come \- non avrà la funzione di range ma verrà selezionato come carattere trattino, ad esempio: [a-zA-Zàèìòù\-]. Senza lo smash (\) il trattino può è interpretato come operatore range e quindi l’intera classe potrebbe essere compromessa.
Operatore $ (dollaro): indica il termine di una riga o semplicemente l’andata accapo.
Negazione: se vogliamo negare una classe descriviamo la seguente sintassi:
[^\-]
L’operatore che si occupa della negazione è il seguente: ^
Nel nostro caso stiamo individuando all’interno di una stringa la ripetizione consecutiva di tutti quei caratteri che non sono il trattino (-). Può essere utile se ci serve individuare una stringa delimitata da un trattino ad esempio: “Se domani non piove è una bella giornata – Se domani piove è una brutta giornata”. La negazione ci ha permesso di individuare una stringa delimitata da il trattino: “Se domani non piove è una bella giornata”.
L’operatore punto (.): se inserito all’interno di un’espressione regolare individua tutti i caratteri tranne l’accapo.
L’operatore di alternanza (|): in programmazione generalmente equivale ad un OR ed analogamente ha lo stesso significato.
Esempio: pippo | pluto individua all’interno di una stringa o la parola pippo o la parola pluto.
I gruppi
E’ possibile raggruppare una serie di classi in un unico gruppo, non per puro masochismo ma se ad esempio volessimo individuare un set di caratteri di cui non conosciamo la lunghezza ma conosciamo la sua regolarità possiamo utilizzare i gruppi. Per rifarci all’esempio iniziale, non so da quanti caratteri è composto il codice del libro però so che al suo interno c’è una fissa regolarità: quattro numeri ed una lettera, quattro numeri ed una lettera, quattro numeri ed una lettera, ecc… Saprò quindi per certo di dover concatenare due classi formate da una ripetizione di numeri e lettere. La ripetizione deve essere valida per almeno una volta (operatore +, ricordate?) e deve terminare con un accapo:
([0-9]{4}[a-zA-Z])+$
Esempio pratico
Appresa un pò di teoria andiamo ad analizzare l’esempio che avevo proposto all’inizio di questo articolo (ricordate? Dovevamo analizzare dei campi di un form contenente Nome libro, Editore, Codice libro e Email). Vabbe forse avete letto tutto ad un fiato la parte teorica e quindi è opportuno riepilogare il caso di studio:
- Nome libro: una stringa che può contenere solo lettere dell’alfabeto (maiuscole o minuscole) più caratteri speciali come le lettere accentate;
- Editore: stesso dicasi per questo campo, può contenere lettere dell’alfabeto con le stesse regole del campo descritto precedentemente;
- Codice libro: è formato da un numero intero separato da un trattino (-);
- Recapito e-mail: stringa suddivisa in 3 parti: ammetterà caratteri alfanumerici e speciali.
Se i miei insegnamenti teorici sono stati validi sicuramente avrete capito come tradurre il tutto in regger, giusto per cronaca ecco qui:
Nome libro: [a-zA-Zàèìòù' ]+
Editore: [a-zA-Zàèìòù' ]+
Codice libro: [0-9]+\-[0-9]+
Recapito email: [a-zA-Z0-9_\.]+@[a-zA-Z0-9-]+\.[a-zA-Z]{0,4}
Forse per alcuni potrebbe spaventare tutto questo papiro di informazioni ma è garantito che una volta capito il tutto potete fare qualsiasi operazione su stringhe possibile ed immaginabile. Per ulteriori dubbi o approfondimenti lascio aperti i commenti.