(regex) Guida pratica alle espressioni regolari

(regex) Guida pratica alle espressioni regolari

Le regex sono il classico argomento di informatica che è difficilmente comprensibile se non viene utilizzato in un contesto reale almeno una volta nella vita. Le espressioni regolari o regex rappresentano infatti dei pattern di testo, ovvero un insieme di più stringhe, frasi o combinazioni delle stesse che è possibile generare per esteso a parte da esse. Una regex non è altro se non una Regular Expression, e contrariamente a quello che si sente dire in giro non servono solo ai programmatori (soprattutto per validare i form, ad esempio): sono utili anche a chi usa Google Analytics, ad esempio, e si possono usare per effettuare editing di testo avanzato in modo veloce oppure, ancora, si possono usare dentro Excel. In molti esempi faremo riferimento alle regex per PHP, ma ciò non toglie che gli esempi si possano estendere anche altrove.

Esempio di regex (facile)

L’esempio più classico che si può pensare riguarda il riconoscimento di indirizzi IP: supponete di essere l’amministratore di una rete, e di avere bisogno, ad un certo punto, di validare un campo della vostra form nel quale l’utente finale inserirà l’indirizzo numerico del tipo 192.168.1.4 o simili. Per assicurarci che il campo che viene inserito sia davvero in indirizzo IP e non una sequenza differenze di caratteri, una regex è proprio ciò che fa al caso nostro.

E’ chiaro anche che non tutte le sequenze di 4 numeri aaa.bbb.ccc.ddd sono valide: la regola fondamentale e più generale possibile vuole che le 4 cifre siano comprese tra 0 e 255. Per indicare un numero compreso tra 0 e 255, come regex scriviamo così:

(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)

dove in sostanza [0-5] rappresenta una cifra qualsiasi tra 0 e 5, mentre il ? indica che quanto scritto poco prima deve essere presente almeno una volta. A questo punto, il riconoscimento (o più correttamente la validazione di questo campo stringa) può essere effettuato con questa espressione compatta:

b(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)b

A prima vista si tratta di aramaico puro, vero? Niente paura. Scomponendola (l’unico modo per capire le regex è proprio questo, visto che si tratta di “cose” complicate composte da tanti pezzetti semplici) ci accorgiamo che è composta da tanti “pezzi” tra parentesi quadre che ricorrono in continuazione: ognuno di essi rappresenta una classe di caratteri, nello specifico dei numeri da 0 a 5 o da 0 a 4 e via dicendo… che possono in alcuni casi comparire o meno almeno una volta (cosiddetto quantificatore rappresentato da ?).

Come funzionano le espressioni regolari

Anzitutto bisogna sapere che un’espressione regolare è fatta di meta-caratteri, ovvero caratteri che rappresentano “entità”, che non necessariamente coincidono con quello che viene scritto, ma che rappresentano dei caratteri generalizzati, ad esempio di un certo tipo (un set di lettere come (a,b,g), o dei numeri).

Ancore di inizio e fine pattern: ^ e $

Per delimitare l’inizio e la fine di un pattern, si può sfruttare il carattere iniziale ^ e quello finale $.

^Sto - qualsiasi stringa che inizia con "Sto"
zzo$ - qualsiasi stringa che finisce con "zzo"

Quantificatori — * + ? {}

I quantificatori servono a definire quante volte un carattere o un simbolo deve essere ripetuto (eventualmente anche “almeno una volta” col simbolo +).

abc*        stringa che inizia esattamente con ab ed è seguita da una c, che potrebbe mancare
abc+        stringa che inizia esattamente con ab ed è seguita da almeno una c
abc?        stringa del tipo ab oppure abc
abc{2}      stringa con ab all'inizio e poi c ripetuta esattamente due volte
abc{2,}     stringa con ab all'inizio e poi c ripetuta almeno due volte
abc{2,5}    stringa con ab all'inizio e poi c ripetuta da due a cinque volte
a(bc)*      stringa a seguita da bc (che potrebbe anche mancare, quindi matcha con: abc e a)
a(bc){2,5}  stringa a seguita da bc da 2 a cinque volte, per cui: abcbc, abcbcbc, ... abcbcbcbcbc.

OR — | or []

a(b|c)     match con ab oppure ac  

Classi di caratteri

\d una cifra

\w una parola

\s uno spazio di qualsiasi genere

\D qualsiasi carattere che non sia una cifra

\W qualsiasi carattere che non sia una parola

\S qualsiasi carattere che non sia uno spazio di qualsiasi genere

Flag (usate in Javascript e PHP)

Le flag servono a delimitare l’espressione regolare, e si possono usare anche senza ” (doppi apici).

Esempi:

/abc/ indica la sequenza abc

/abc/i indica la sequenza abc case insensitive (sia AbC che ABC che ABc ecc.)

Gruppi

Non voglio sembrare mistico o astratto, a questo punto: voglio soltanto esprimere che posso avere dei meta-caratteri che rappresentano “gruppi” o “sequenze” jolly, per così dire, di possibili stringhe. Invece di scrivere “verifica che sia un numero tra 0 e 9” scriverò in meta-caratteri semplicemente la “dichiarazione” [0-9], dove le parentesi quadre indicano un gruppo.

Pattern e quantificatori

Notiamo poi che scrivere (.)+ significa esprimere un pattern, ovvero far capire al linguaggio che con quell’espressione vogliamo catturare tutte le stringhe di lunghezza almeno 1 di qualsiasi caratteri (da cui deriva il +). Quindi . rappresenta (nelle regex cosiddette POSIX usate da PHP, in particolare) un numero qualunque di caratteri di cardinalità (numero di caratteri) almeno uno.

Quindi un generico (.)+ puo’ rappresentare varie stringhe corrispondenti:

f

asg

1265763

bads

sjhdjha

salvatore capolupo

ma non, ad esempio, la stringa vuota perchè + esprime il fatto che la stringa sia di almeno un carattere. Se invece scrivo (.)? dico che la mia stringa qualsiasi potrebbe anche essere vuota (come un campo “non richiesto” del modulo di iscrizione di una form).

Validare un indirizzo email con una regex

Vediamo di comprendere come riconoscere un indirizzo e-mail: la sua struttura più generale possibile sarà, ad esempio, del tipo [email protected]. Quello che dovremo fare è:

1) far capire all’interprete che @ e . sono caratteri “letterali” e non meta-caratteri (altrimenti ci sarebbe ambiguità), e

2) esprimere “genericamente” username e address e est.

In altri termini potremmo scrivere:

(.)[email protected](.)+.(.){3}

dove (.)+ ha lo stesso significato di prima, @ e . indicano i caratteri @ e . e (.){3} indica una sequenza di caratteri lunga, per semplificare, esattamente tre (potremmo avere infatti com, net, ma non it, ad esempio). Dovrebbe essere tutto un po’ più chiaro, spero.

A questo punto la composizione di un indirizzo IP xxx.xxx.xxx.xxx si può suddividere in 4 numeri separati dal punto:

(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)

Le alternative sono separate dal meta-carattere |, cioè stiamo dicendo che un singolo numero di un IP può essere dato da:

  • 25[0-5] ovvero ’25’ seguito da un numero qualsiasi compreso tra 0 e 9 (250,251,…,255)
  • 2[0-4][0-9] ovvero ‘2’ seguito da una cifra tra 0 e 4 ed una cifra tra 0 e 9 (247, 209, ecc.)
  • [01]?[0-9][0-9] ovvero uno 0 o un 1 (che possono mancare, dato che sono seguiti da ?), seguito da due cifre qualsiasi tra 0 e 9 (17, 124, ecc.)

L’indirizzo IP,in definitiva, sarà dato da una concatenazione di 4 volte questo pattern, che accetterà dunque stringhe tipo: 255.255.0.0 ma non 256.11.17.27 (il 256 è fuori range). L’argomento è difficile (almeno all’inizio) ma molto appassionante per quanto mi riguarda, e soprattutto ricordiamo che non è fine a se stesso perchè viene utilizzato nella comune programmazione PHP come nella sintassi dei file htaccess di Apache.

0 voti


Informazioni sull'autore

Salvatore Capolupo

Consulente SEO, ingegnere informatico e fondatore di Trovalost.it, Pagare.online, Lipercubo.it e tanti altri. Di solito passo inosservato e non ne approfitto.