Guida alle espressioni regolari (regex)

Guida alle espressioni regolari (regex)

13513
12
CONDIVIDI

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.

Recensioni
Autore
Gianpaolo
Data
Articolo
Guida alle espressioni regolari (regex)
Voto
5
  • Marino

    questa è sbagliata: ([0-9]{4}[a-zA-Z)+$
    deve essere così: ([0-9]{4}[a-zA-Z])+$

  • Mario Concina

    oops, dimenticata la parentesi.
    Sistemo, grazie!

  • Filippo

    ciao è passato un pò di tempo da quando l’hai scritto…
    ma vorrei sapere come posso scrivere la regex per un url tipo:
    http://www.sito.it/categoria/sottocategoria..

    dove categoria e sottocategoria sono definiti in un databese e cambiano

    grazie

    • Mario Concina

      probabilmente immagino una cosa simile:

      RewriteEngine On
      RewriteRule ^([a-zA-Z0-9_-]+)$ categoria.php?notizia=$1

      ovviamente la stringa “categoria.php?notizia=” cambia in base al formato della tua querystring.

  • Esperanto

    Non esiste un qualcosa che permetta di catturare le lettere di TUTTE le lingue, almeno quelle basate sull’alfabeto latino?
    Dovrei controllare un campo form con il nome, ma dovrebbe essere internazionale. Quindi accettare ogni tipo di nome, che ovviamente memorizzerò nel DB come UTF-8.
    Se scrivo A-Z non mi considera, per dire, ?. Probabilmente non servirà, ma per curiosità vorrei trovare la soluzione a questo problema una volta per tutte.
    Qualche suggerimento, ad esempio usare la negazione degli insiemi fino a far rimanere solo le lettere inclusi caratteri strani, come dovrebbe essere?
    Grazie

  • Esperanto

    (P.S.) nel mio precedente commento c’è un punto interrogativo che veramente dovrebbe essere un carattere accentato preso a caso (una zeta con l’accento, dal polacco). Ma dopo l’invio del commento è stata persa.

  • marco

    Salve
    io dovrei riuscire a scriver il n 180…
    ovvero devo individuare questo numero che poi mi fa scattare un trigger di una segnalazione.
    Come si puo scrivere in termini di espressioni regolari
    grazie anticipatamente ciascuno di voi

  • Gerry

    sono alle “prime armi” con le regular expression… potreste aiutarmi a capire se e come si potrebbe fare una sostituzione di stringhe di questo tipo:
    ho una stringa di caratteri che può contenere valori di questo tipo: /Date(1386543600000+0100)
    vorrei quindi sostituire tutte le stringhe di tipo /Date( *qualsiasi cosa qua dentro* ) con la stessa stringa ma aggiungendo dei caratteri all’inizio e fine in questo modo: \\/Date(*il valore originale*)\\
    ringrazio in anticipo per il vs aiuto

  • Grazie mille ottimo articolo

  • Carlo

    Salve ragazzi, dovrei scrivere la regex di questa espressione:

    61432c789518088e2c708f122d260f9178b87059 – – [04/Mar/2014:06:25:04 +0100] “GET /page/Stig_Boqvist HTTP/1.1” 200 4561 “-” “Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)”

    Qualcuno potrebbe aiutarmi??
    Grazie

  • max

    graziedel documento.
    ma vorrei un chiarimento

    se in un testo devo intercettare un NOT Re: ed un FW:
    con questo comando ([R][e][:]) definisco intercetta Re: ma non in negazione, inoltre non capisco come inserire anche un operatore OR tipo: ([R][e][:]) OR ([F][W][:]).
    mi potete aiutare?
    thx

  • Alfonso Savino

    Ciao a tutti

    ho un problema enorme

    ho un log con diversi campi, posso utilizzare solo le regex;
    il primo campo è sempre f= è rappresenta un indirizzo mail mittente
    il secondo e cosi via sono tutti t= t= t= t= … e rappresenta tutti i destinatari(che possono variare)
    devo riuscire a mettere ogni mail destinataria in variabili distinte. Come devo fare?
    al momento sono a questo punto f= t=