Informatica di base

Lookbehind e lookahead nelle espressioni regolari

Autore

Manuel Ricci

Finora abbiamo visto vari modi per individuare delle sottostringhe in un testo grazie all’ausilio delle regular expression, ma nessuno di questi ci ha permesso di fare un controllo prima di restituirci la corrispondenza.

Per assolvere a questa funzione esistono i lookaround, più nello specifico lookahead e lookbehind. Vediamoli.

Lookahead

Le istruzioni di lookahead sono due:

  1. (?=...): si chiama positive lookahead e controlla se l’espressione è seguita da ciò che viene definito dopo l’uguale.
  2. (?!...): si chiama negative lookahead e controlla se l'espressione è seguita da ciò che viene definito dopo il punto esclamativo.

Facciamo un esempio pratico supponendo di avere questo testo:

11010$ 10£

Se volessimo individuare tutti i numeri seguiti dal simbolo del dollaro sappiamo che la regex sarà \d+[$|€], ma se non volessimo la valuta, ma solo la cifra? Allora in quel caso un positive lookahead fa al caso nostro.

1\d+(?=\s*[$|€])

In questo caso controllo anche la presenza di zero o più spazi per quelle cifre che potrebbero essere anche scritte 10 €, ma il risultato è proprio quello che speravamo e cioè un match solo sui 10 sia che siano seguiti dal simbolo dell’euro che dal simbolo del dollaro.

Se invece avessi un testo come questo:

1Articolo: 10 panini    10

E fossi interessato ad individuare un numero seguito da uno spazio e che ad un certo punto dopo la corrispondenza ci sia il simbolo dell’euro possiamo scrivere:

1\d+(?=\s)(?=.*€)

Per quanto inusuale come esempio è un modo per mostrarvi come i lookaround possono essere anche messi uno di seguito all’altro affinché tutto le condizioni vengano soddisfatte.

Oppure ancora:

1lola

Se fossi interessati a selezionare tutto le lettere che non sono seguite da una o, possiamo scrivere:

1[A-Za-z](?!o)

In questo caso ho usato un negative lookahead perché esclusivamente interessato alle lettere non seguite dalla o.

Lookbehind

Per il lookbehind abbiamo due comandi molto simili a quelli già visti con lookahead:

  1. (?<=...): positive lookbehind che controlla se l’espressione è preceduta da ciò che viene definito dopo l’uguale.
  2. (?<!...): negative lookbehind che controlla se l’espressione non è preceduta da ciò che viene definito dopo l’uguale.

La differenza sostanziale oltre al fatto che c’è una parentesi angolare tra il ? e = e tra il ? e ! sta nel fatto che rispetto al lookahead il lookbehind viene messo a sinistra dell’espressione regolare.

L’espressione regolare vista in precedenza con le valute, qualora volessimo controllare che dei numeri siano preceduti dal simbolo di valuta possiamo scrivere:

1(?<=[$|€])\d+

Limitazioni di lookbehind

I più attenti avranno notato che rispetto a prima ho rimosso il \s*, questo perché i lookbehind in alcuni engine hanno delle limitazioni. Le espressioni regolari poste al loro interno devono essere “finite”, cioè non ci possono essere quantificatori.

Questa scelta è legata alle performance. L’engine non saprebbe quanto andare indietro e quindi si è preferito rendere l’operazione non possibile.

Questo discorso però non è valido per tutti gli engine, alcuni come quelli di .NET e Java consentono il lookbehind indefinito.

Lookaround e backreference

Avrete sicuramente notato che usiamo le parentesi tonde per definire un lookaround. Questo comporta quindi anche la creazione di una backreference? Risposta breve no.

Nonostante l’uso delle parentesi tonde, i comandi di lookaround non creano anche una backreference. Per poterlo fare dobbiamo usare un ulteriore coppia di parentesi tonde, in questo modo:

1(?<=(\$|€))\d+
2\d+(?=\s*(\$|€))

Le espressioni nei lookaround sono state incluse in una coppia di parentesi tonde e questo ci permetterà di accedere al gruppo attraverso la sintassi vista nella lezione sui gruppi.

Condizioni

Sempre rimanendo in tema di gruppi e lookaround, abbiamo la possibilità anche di scrivere delle condizioni, molto simili a degli if/else.

Supponendo di avere questo testo:

1Il tofu è buono o disgustoso

Possiamo scrivere una condizione in questo modo:

1(?(?=\xE8)(è buono)|(disgustoso))

La prima coppia di parentesi tonde è la condizione, la coppia (?=\xE8) può avere match positivo o negativo. Nel caso di positivo verrà quindi selezionato è buono, altrimenti in caso di condizione falsa disgustoso.

Nota: \xE8 è la è codificata in UTF-16

In pratica

Supponendo di avere questa serie di numeri

1100 -34 -12 43 23 -3

Individuare i numeri positivi.

Soluzione

Subito si può pensare di scrivere un’espressione simile a questa:

1(?<!-)\d+

Quello che però stiamo cercando è quindi un numero che non venga preceduto dal -, questo significa che il 4 e il 2 di -34 e -12 corrispondo. Dobbiamo affinare la regex con un ulteriore lookbehind:

1(?<!-)(?<!\d)\d+

Questo farà sì che i numeri vengano matchati correttamente. Tra l’altro questa regex può essere semplificata così:

1(?<![-\d])\d+

E rimane comunque valida.

Conclusioni

Detto questo quindi anche questa lezione sui lookaround nelle espressioni regolari ce la portiamo a casa, nella prossima, penultima lezione, vedremo tutti quei caratteri rimasti fuori da tutto ciò che abbiamo visto finora, una panoramica generale per sapere che esistono.

Caricamento...

Diventiamo amici di penna? ✒️

Iscriviti alla newsletter per ricevere una mail ogni paio di settimane con le ultime novità, gli ultimi approfondimenti e gli ultimi corsi gratuiti puubblicati. Ogni tanto potrei scrivere qualcosa di commerciale, ma solo se mi autorizzi, altrimenti non ti disturberò oltre.

Se non ti piace la newsletter ti ricordo la community su Discord, dove puoi chiedere aiuto, fare domande e condividere le tue esperienze (ma soprattutto scambiare due meme con me). Ti aspetto!

Ho in previsione di mandarti una newsletter ogni due settimane e una commerciale quando capita.