Guida sulle Espressioni Regolari in Italiano

Una guida on-line per capire e rendere vostro il magnifico mondo delle espressioni regolari

Introduzione alle espressioni regolari

Vi sarà capitato spesso di sentire parlare delle espressioni regolari e di quanto esse siano utili in molti ambiti e se siete qui probabilmente non avete un idea chiara di cosa siano esattamente e di come vanno usate le espressioni regolari. Per iniziare bisogna chiarire il fatto che le espressioni regolari hanno molti nomi, come: regex, regexp o reg-ex, Tutte indicano la stessa cosa.

A volte la storia è noiosa ma vivere senza, equivale a vivere senza memoria, quindi chi invento le espressioni regolari? Stephen Kleene definì nel 1950 i "regular sets", il primo concetto da cui originarono le espressioni regolari. La prima applicazione delle espressioni regolari fu implementata da Ken Thompson nel 1966 con l'editore "QED". Oggi tutti i principali linguaggi di programmazione supportano le espressioni regolari. Molti editori di testo e probabilmente tutti gli "IDE" supportano le espressioni regolari.

La cosa importante da ricordare di questa introduzione è che le espressioni regolari sono usate per cercare e sostituire parti di testo all'interno di un testo più grande.

Espressioni regolari = CERCA && SOSTITUISCI

Cosa sono le espressioni regolari?

Le espressioni regolari sono semplici sequenze di caratteri che hanno un particolare significato e che ci aiutano a cercare e sostituire stringe all'interno di altre stringe. Una stringa in ambito informatico è una sequenza di caratteri, quindi utilizziamo sequenze di caratteri per cercare e sostituire altre sequenze di caratteri.

Questa guida è principalmente rivolta a informatici, ma credo che ogni impiegato d'ufficio dovrebbe avere una buona conoscenza delle espressioni regolari. Va detto comunque che la complessità delle espressioni regolari fa si che molti anche nell'ambito informatico non le prendano in considerazione.

Mi capita spesso di dovere modificare del testo o cercare del testo all'interno di un documento. Davanti ad un problema del genere la prima cosa che un informatico farebbe sarebbe quella di scrivere del codice. Facciamo un esempio: Voglio trovare la parola "Eva" all'interno della frase "Ogni parola che Eva disse fece leva nei nostri cuori che ardevano. Tutti amano Eva.", scrivere del codice per questo problema può essere molto più difficile di quello che si può pensare. Il codice scritto dovrebbe prevedere molte problematiche, bisogna distinguere tra maiuscole e minuscole, trovare la parola esatta, non all'interno di un'altra parola e cosi via. Per tutti questi casi dovremmo scrivere un sacco di codice.

È in questi casi che ci vengono in aiuto le espressioni regolari. Con le espressioni regolari il lavoro si semplifica notevolmente con una semplice espressione regolare: \beva\b

Caratteri speciali nelle espressioni regolari

Abbiamo appena mostrato l'espressione regolare:
\beva\b

Con questa espressione regolare possiamo trovare tutte le occorrenze della parola eva all'interno di un documento di testo.
Ci sono 7 caratteri in questa espressione regolare:
\ b e v a \ b
5 caratteri regolari e 2 barre rovesciate. L'utilizzo della barra rovesciata o "backslash" da al carattere normale un significato speciale. Il duo speciale di caratteri \b indica il confine di una parola.

Fai una ricerca globale, della parola "Eva":





Ci sono molti caratteri che se preceduti da una barra rovesciata vengono considerati caratteri speciali.

Lista di caratteri speciali doppi preceduti da una barra rovesciata.

\a	Suono acustico (Bell Code).
\A	Inizio di testo.
\b	Confine di una parola.
\B	Tutto tranne \b.
\c	Codice di controllo (Ctrl).
\d	Valore numerico.
\D	Tutto tranne \d.
\e	Codice escape.
\f	Codice fine pagina.
\n	Codice fine riga.
\r	Codice ritorno carrello.
\s	Spazio.
\S	Tutto tranne \s.
\t	Codice di tabulazione.
\u	Codice unicode.
\v	Codice di tabulazione verticale.
\w	Carattere di parola.
\W	Tutto tranne \w.
\x	Codice ASCII.
\z	Fine del testo.

Lista di caratteri speciali singoli.

^	Inizio della riga.
$	Fine della riga.
*	Zero o più.
+	Uno o più.
?	Zero o uno.
.	Qualsiasi carattere tranne \n.
( )	Gruppo di caratteri.
{ }	Numero di ripetizioni.
[ ]	Gruppo di caratteri.
|	Stringhe alternative.
/	Modificatore di gruppi.
\	Carattere speciale per dare o rimuovere significato a altri caratteri.

Ognuno dei caratteri speciali singoli qui elencati se preceduti da una barra rovesciata diventa un carattere normale. Quindi la barra rovesciata ha due utilità. Dà significato speciale a caratteri normali e lo toglie ad altri considerati speciali.
Se dobbiamo ricercare il carattere barra rovesciata dobbiamo semplicemente mettere un altra barra rovesciata davanti. Quindi scrivere due barre rovesciate in un espressione regolare \\ .

Ricerche semplici

La ricerca più semplice che si può fare è quella di ricercare una stringa semplice, quindi una sequenza semplice di caratteri. Per esempio considerate di cercare l'estensione di un file "jpeg".

Fai una ricerca globale, della parola ".jpg":





La ricerca non ha prodotto il risultato che si pensava. In fatti l'espressione regolare ha trovato anche la stringa ijpp.
Questo comportamento è dovuto grazie al carattere "." il quale è un carattere speciale che trova corrispondenza con qualsiasi carattere normale. Per trovare la corrispondenza esatta di .jpg bisogna mettere una barra rovesciata davanti al carattere speciale cosi da avere l'espressione regolare \.jpg. Se scriviamo il punto da solo, trova corrispondenza con tutti i caratteri, invece se vogliamo trovare solo il punto, scriviamo la regex \..
Provate a sostituire l'espressione regolare .jpg con l'espressione regolare \.jpg cosi da vedere che corrispondenza viene trovata.

Valori numerici e spazi

Molto spesso si ha bisogno di cercare numeri all'interno di documenti di testo in posizioni specifiche. A volte si cerca del testo tranne valori numerici. Questi risultati si possono ottenere con l'utilizzo dell'espressione regolare \d che seleziona valori numerici o meglio i caratteri tra 0 – 9. Di grande aiuto ci torna anche l'espressione regolare complementare \D che seleziona tutto tranne valori numerici.

Ci sono due espressioni regolari simili alle due precedenti, esser pero selezionano gli spazi e tutto tranne gli spazi. Esse sono le espressioni regolari \s per gli spazi e \S per tutto tranne gli spazi.

I valori numerici \d e \D

Pensate alla situazione nella quale dobbiamo trovare una data, magari il compleanno di una persona scritto in un documento molto grande che contiene molti altri numeri, e l'unica cosa che sapete è che il formato del compleanno è nella forma gg/mm/aaaa. Quindi come dobbiamo procedere? Semplice, usiamo l'espressione regolare \d.
Ognuno dei \d corrisponde ad un numero tra 0 e 9, quindi per trovare una data nella forma 05/01/1909 dobbiamo scrivere una regex nella forma \d\d/\d\d/\d\d\d\d dove i primi \d\d corrispondono a 05 poi la barra è equivalente alla barra, i secondi \d\d a 01 e cosi via.

Fai una ricerca globale, della data nel formato gg/mm/aaaa:





Cosa succede se usiamo l'espressione regolare \D? Provate a sostituire \d\d/\d\d/\d\d\d\d con \D per vedere il risultato.

Gli spazi: \s e \S

Utilizzando le espressioni regolari possiamo distinguere tra spazi e altri caratteri. Per fare ciò utilizziamo le regex \s e \S.

Fai una ricerca globale, degli spazi:





Ora se provate a sostituire \s con \S potrete vedere come cambia il risultato.

Il gruppo o set di caratteri [ ]

Spesso abbiamo bisogno di cercare un gruppo di caratteri all'interno di un testo. Per fare ciò si usa un'espressione regolare particolare che fa la ricerca su un gruppo o set di caratteri [abcd] dove il gruppo di caratteri cercati è a, b, c, d.
Il gruppo di caratteri più semplice che possiamo trovare è un singolo carattere, che è come se cercassimo il carattere stesso. L'espressione regolare [a] equivale alla regex a.

Gruppo di caratteri [abc]

Se volgiamo cercare un gruppo di caratteri scriviamo la regex [abc] dove I caratteri che stiamo cercando sono a, b, c.

Fai una ricerca globale, e trova tutti i colori html che hanno valori carattere:





Sequenza di caratteri [-]

Se vogliamo cercare una sequenza di caratteri dobbiamo usare la regex [0-9] dove i caratteri che stiamo cercando sono i numeri da 0 a 9. Per esempio se stiamo cercando tutte le lettere dell'alfabeto Italiano, scriviamo la regex [a-zA-Z], siccome in Italiano ci sono anche le lettere accentate, le dobbiamo aggiungere, quindi la regex diventa [a-zA-Zàòéìù].

Fai una ricerca globale, e trova tutti i colori html con valori numerici:





Tutti i caratteri tranne [^]

A volte dobbiamo trovare un gruppo di caratteri tranne alcuni, per fare ciò usiamo la regex gruppo di caratteri con l'aggiunta del carattere ^. Per esempio se vogliamo trovare tutti i colori numerici html ma solo quelli con valori diversi da 0 a 4. Per fare ciò scriviamo l'espressione regolare [^0-4]

Fai una ricerca globale, e trova tutti i colori html numerici con valori diversi da 0,1,2,3,4:





Si può usare il carattere ^ come elemento del gruppo, l'unica accortezza è quella di non metterlo in prima posizione dove assume il valore di carattere speciale.

Le parole: \b, \B, \w, \W

A questo punto possiamo cercare caratteri, numeri e gruppi di caratteri e numeri. Abbiamo visto prima il carattere speciale \b (confine di una parola), ora vedremo l'opposto di questo carattere speciale, il carattere speciale \B e gli identificatori di parola \w e \W

I confini di una parola: \b

Abbiamo visto l'utilizzo della regex \b per trovare parole. Ora vogliamo trovare testo di una certa lunghezza. Per fare ciò possiamo sempre usare la regex \b.

Fai una ricerca globale, e trova tutte le parole con tre caratteri:





Come potete vedere abbiamo un problema, abbiamo selezionato anche parole di due lettere come te o tu, questo perché il carattere speciale . Trova corrispondenza anche con il carattere spazio.

Per risolvere il problema possiamo usare il gruppo di caratteri come regex.

Fai una ricerca globale, e trova tutte le parole di tre caratteri:





Una cosa molto importante da sottolineare qui è che il carattere speciale \b trova corrispondenze di lunghezza 0 caratteri, il che vuol dire che si comporta come un ancora, stabilisce il confine di altri caratteri.

I caratteri di una parola: \w

Invece di usare il gruppo o sequenza di caratteri nella regex [a-zA-Z] potremmo usare la regex \w

Fai una ricerca globale, e trova tutte le parole di tre lettere:





Possiamo a questo punto usare questa semplice regex, invece di scrivere ogni volta la sequenza di caratteri.

I caratteri inversi di confine e caratteri di parola: \B e \W

La regex \W rappresenta tutto tranne una parola. La definizione di una parola secondo le espressioni regolari è una sequenza di caratteri, numeri e il carattere _. Secondo questa definizione anche i numeri e il carattere _ possono essere usati all'interno di una parola.
Per capire meglio il comportamento, ci viene in aiuto l'esempio sottostante.

Fai una ricerca globale, per vedere l'utilizzo della reg-ex \W:





Per vedere il comportamento inverso provate a sostituire nella reg ex il carattere speciale \W con \w

L'utilizzo di \B è un po più complicato. Prima di tutto l'espressione regolare \B si comporta come \b un ancora di lunghezza 0, ed è l'inverso di \b.

L'espressione regolare \b è un ancora che trova come corrispondenza il confine di una parola. Al contrario la regex \B che è l'inverso di \b, trova corrispondenza in tutto ciò che non è il confine di una parola. Il che significa che trova corrispondenza nei confini dei caratteri che non sono confini di parole. Avete capito qualcosa? Per schiarire il concetto seguite l'esempio qui sotto.

Fai una ricerca globale, per capire l'utilizzo della reg-ex \B:





Come si vede dal esempio la regex trova corrispondenza con on sulla parola "nonno" ma non con la parola "Non", questo perché la parola Non finisce con un confine di parola con il quale la regex non trova corrispondenza. Per trovare la stringa on su Non basta cambiare la regex con \Bon\b.

Provate a sostituire \Bon\B con \Bon\b e vedere cosa succede.

Inizio, fine di una riga e tutto il testo ^, $, \A, \Z

Spesso abbiamo bisogno di definire l'inizio o la fine di una riga, oppure tutto un testo che dobbiamo analizzare. Per ottenere questo risultato usiamo le regex ^, $ per le righe e le regex \A, \Z per il testo intero.

Inizio e fine di una riga ^, $

Siamo nel caso dove dobbiamo trovare tutte le parole con un trattino e che sono alla fine di una riga, tipicamente le parole separate per l'inizio di una nuova riga. Per assolvere a questo problema possiamo usare le regex.

Fai una ricerca globale, trova tutte le parole separate a fine riga con un trattino:





Come si può vedere dal esempio la regex trova corrispondenza con la stringa qaulc- ma non la stringa reg-.

Inizio e fine del testo \A, \Z

Pensate alla situazione nella quale abbiamo un file con dei tag di inizio e fine riga ma vogliamo trovare solo il tag di inizio file. Purtroppo queste regex non possono essere usate in javascript ma si ha la possibilità di usarle in altri linguaggi.

I quantificatori: +, *, ? e { }

Può capitare di avere la necessità di fare ricerche su stringhe di lunghezza variabile. Anche in questo caso ci tornano in aiuto le regex, in questo caso usiamo i quantificatori. Essi possono essere posizionati dopo altri regex e in questo modo dare un nuovo significato alla regex. Per esempio se con la regex \d possiamo cercare un valore numerico all'interno di un testo, con la regex \d+ possiamo trovare uno o più numeri consecutivi all'interno del testo.

Uno o più: +

Pensate al caso in cui vogliamo trovare tutte le date all'interno di un testo. Conosciamo il formato in cui le date sono scritte, quindi con due barre ma non sappiamo se sono scritte nel formato gg/mm/aaaa o nel formato aaaa/mm/gg. In questo caso in aiuto ci arriva la regex +.

Fai una ricerca globale, trova tutte le date all'interno di un testo:





Come potete vedere la regex \d+ trova corrispondenza con uno o più valori numerici, in questo caso con 2 o 4 valori. Si può anche usare la regex [0-9] per la quale va usata la forma [0-9]+.

Zero più: *

Pensate a un caso più complicato nel quale dobbiamo trovare delle date all'interno di un testo ma dove le date sono espresse in numeri romani come nell'esempio 01/XII/2016 oppure anche date normali con solo numeri 2016/12/01.

Fai una ricerca globale, trova tutte le date nel testo:





Come potete vedere la regex trova corrispondenza su entrambe le date. La regex * significa zero o più ripetizioni dei caratteri che essa segue.

Zero o uno: ?

In questo caso abbiamo date nel formato ancora più limite, gg/mm/aaaa oppure ggmm/aaaa.

Fai una ricerca globale, trova tutte le date nel testo:





Come si vede dal risultato del esempio, mettere la regex ? Dopo il carattere / fa si che si possa trovare corrispondenza la data anche se in alcuni case la barra o "slash" / non è presente come nel caso della data 0112/2016.

Numero di ripetizioni: { }

Esiste un caso nel quale le regex viste fino ad ora non possono risolvere. Il caso di date nei formati ggmmaaaa oppure gg/mm/aaaa. Se usiamo le regex viste fino ad ora, e quindi i quantificatori visti fino ad ora avremo un caso del genere.

Fai una ricerca globale, trova tutte le date nel testo:





In questo esempio la regex ha trovato corrispondenza anche con il numero di telefono, cosa che non vogliamo che succeda. Per risolvere questo problema possiamo usare la regex { }.

Ci sono vari casi nel quale possiamo usare la regex { }:

  1. {numero} in questo caso usiamo solo il numero dentro le parentesi, significa un esatto numero di ripetizioni. Esempio: \d{3} trova corrispondenza con le sequenze di numeri con esattamente tre numeri.
  2. {numero1, numero2} – in questo caso usiamo due numeri dentro le parentesi separate da una virgola, significa ripetizioni da numero1 a nuemro2 ripetizioni. Esempio: \d{2,5} trova corrispondenza tutti I numeri che hanno da 2 a 5 valori numerici. Per esempio la regex precedente troverà corrispondenza con i numeri 12, 123, 1234, 12345 ma non i numeri 1 o 123456.
  3. {numero, } - in questo caso usiamo un numero seguito da una virgola, significa un numero di ripetizioni da numero a indefinito. Esempio: \d{3,} la regex troverà corrispondenza con i numeri 123, 1234, 12345 e a salire, ma non con i numeri 1, 12
  4. {numero, numero} equivale a {numero}
  5. {1,} equivale a +
  6. {0,} equivale a *
  7. {0,1} equivale a ?

Quindi per risolvere il problema visto sopra utilizzando la regex { }.

Fai una ricerca globale, trova tutte le date dentro un testo:





Finalmente abbiamo trovato la corrispondenza desiderata.

Pigrizia: ?

Tutte le espressioni regolari sono ingorde. Per spiegare questo concetto usiamo l'esempio qui sotto.

Fai una ricerca globale, trova tutte le operazioni matematiche tra parentesi:





Il risultato ottenuto non è esattamente quello che ci aspettavamo. Noi ci aspettavamo che venisse trovata corrispondenza solo col le operazioni di somma, ma la regex ha trovato corrispondenza con l'intera operazione matematica. Perché? Questo è avvenuto perché la regex è ingorda. Analizziamo la regex.
Abbiamo \(.+\), la prima parte \( trova corrispondenza con la prima parentesi ( del operazione matematica. Poi la .+ invece di trovare corrispondenza solo con la parte 3+2, fa corrispondenza con tutta la restante operazione 3+2)*(4+5)*(1+6). Poiché la regex . trova corrispondenza con tutti i caratteri tranne la linea di fine riga, trova corrispondenza anche con le parentesi ( e ). E poiché è ingorda continua a trovare corrispondenza fino all'ultimo carattere trovato. Per rimediare a questo comportamento usiamo la regex ? che rende le espressioni regolari pigre. Se posizionata dopo la regex .+ produce la versione pigra .+?, in questo modo la regex ferma la corrispondenza una volta trovata l'operazione matematica 3+2. Per chiarire il tutto seguite l'esempio qui sotto.

Fai una ricerca globale, trova tutte le operazioni matematiche tra parentesi:





Gruppi

Come in altri linguaggi le regex hanno la funzionalità di mettere in gruppi cosi che certi casi particolari viene trovata corrispondenza.

Gruppo: ( )

Abbiamo imparato l'utilizzo della regex + ma pensate al caso in cui vogliamo applicare la ripetizione non ad un solo carattere ma a dei caratteri consecutivi.

Fai una ricerca globale, trova tutte le ripetizioni dei caratteri consecutivi 01:





Backreference o riferimento all'indietro

Quando utilizziamo le parentesi rotonde creiamo un gruppo. La corrispondenza creata dal contenuto del gruppo può essere riutilizzata per trovare altre corrispondenze o per fare delle sostituzioni. Per fare questo tipo di corrispondenza che si associa al gruppo usiamo la regex \n-th che si chiama backreference o riferimento all'indietro.

Fai una ricerca globale, trova tutte le parole palindrome di quattro caratteri:





In questo esempio la prima (\w) trova corrispondenza con la prima A di Anna, la seconda (\w) con la prima n di Anna poi la \2 backreference associata con la seconda (\w) trova corrispondenza con la seconda n di Anna e alla fine la \1 trova corrispondenza con la prima A di Anna.

Gruppo passivo: (?:)

Ogni volta che usiamo le regex di gruppo creiamo la backreference, questo ha un grande costo in termini di processore e memoria usata. Per creare un gruppo ma che non crei la backreference possiamo usare il gruppo passivo che trova corrispondenza con i caratteri contenuti tra le parentesi ma che non ci da la possibilità di usare la regex "\n-th" poiché probabilmente verrà usata poche volte.

Alternativa: |

Spesso abbiamo bisogno di trovare corrispondenza con gruppi alternativi di caratteri, per risolvere questo problema usiamo la regex alternativa | o carattere barra verticale o pipe.

Fai una ricerca globale, trova tutti i numeri mobili di 10 caratteri:





In questo modo abbiamo creato una corrispondenza per le due possibili alternative di prefisso internazionale.

Guarda intorno: (?!), (?=)

In questo paragrafo vedremo l'utilizzo della funzionalità più avanzata delle espressioni regolari, ovvero la capacità di trovare corrispondenze in avanti oppure indietro rispetto al punto in cui si trova la corrispondenza attuale della regex.

Guarda in avanti se diverso: (?!)

Il significato di questa regex è, trova una corrispondenza con i caratteri non seguiti dal valore nella regex.

Fai una ricerca globale, trova tutte le email che non contengono l'estensione .org:





Guarda in avanti se uguale: (?=)

Il significato di questa regex è, trova corrispondenza con i caratteri seguiti dal valore contenuto nella regex.

Fai una ricerca globale, trova tutte le email che contengono l'estensione .org:





Guarda indietro

L'uso di queste regex è simmetrico a quelle di guarda in avanti ma non sono supportate in javascript.

I modificatori: i, g, m

Le espressioni regolari sono scritte nella forma di stringhe o sequenza di caratteri che se utilizzate all'interno di funzioni javascript sono scritte tra due barre seguite da alcuni modificatori come /\ban\b/mgi, dove \ban\b è la regex racchiusa tra due barre ed è seguita dai modificatori mgi. Ci sono tre tipi di modificatori javascript i, g e m.

Insensibilità alle maiuscole: i

Se si utilizza questo modificatore in un espressione regolare fa si che la ricerca nel testo sia insensibile alle maiuscole. Che vuol dire che la ricerca produrrà una corrispondenza indipendentemente dalla presenza di caratteri scritti in maiuscolo o in minuscolo. Se modifichiamo il primo esempio e mettiamo i modificatori da mgi a mg vedremo che non si troverà una corrispondenza con niente. Poiché tutte le parole "Eva" hanno la prima lettera in maiuscolo.

Fai una ricerca globale, della parola "Eva":





Se aggiungiamo il modificatore i vedremo che la corrispondenza viene trovata.

Multi linea: m

Se usiamo il modificatore multi linea, le ancore ^ e $ troveranno corrispondenza all'inizio e alla fine della riga invece che all'inizio e alla fine del documento intero, che si avrebbe nel caso in cui non utilizziamo il modificatore.
Se non usiamo il modificatore la regex ^ equivale alla regex \A e la regex $ equivale alla regex \Z.

L'ultimo modificatore è il modificatore di ricerca globale. Ci permette di fare ricerche che si susseguono. E ricominciare in questo modo la ricerca dalla corrispondenza precedente. Se non usiamo questo modificatore ricerche consecutive daranno lo stesso risultato.