Inviato da Marco Brenna il Mer, 11/01/2023 - 01:10
Java inferno

A volte bisogna dire le cose come stanno: Usare le espressioni regolari in Java fa proprio schifo.

Non tanto per le espressioni regolari in se ma per un piccolo dettaglio che non è stato considerato o è stato volutamente ignorato da chi ha sviluppato il linguaggio: essendoci sempre le virgolette doppie trattandosi di stringhe bisogna fare l'escape del backslash... che nelle Regex c'è praticamente ovunque!

E a rendere ancora più ingombrante la cosa, spesso ci si ritrova a dover cercare nella stringa proprio il carattere backslash... si arriva così a situazioni al limite del grottesco come la seguente:

Per rimuovere il backslash in fondo ad un path relativo all'ambiente Windows e cancellare eventuali spazi: myDirPath = myDirPath.replaceAll("\\\\\\s*$", "");

Ovviamente esistono soluzioni più eleganti e comprensibili per fare la stessa operazione ma è giusto per darvi una idea di quanto questa questione dei backslash sia pesante in termini di scrittura del codice, di comprensione e anche semplicemente alla vista.

Che dite, proviamo a capire perchè ce ne vanno così tanti?

Per prima cosa "\" preso da solo in una stringa non è qualcosa di valido in quanto con le doppie virgolette il backslash si usa per specificare alcuni caratteri speciali proprio delle stringhe; l'esempio più famoso è \n che si legge tutto insieme come "a capo". Quindi il primo backslash è sempre relativo a questo uso speciale e va abbinato ad un'altro backslash per indicare "il carattere \". Questo significa che i backslash intesi come carattere in Java andranno sempre a coppie di due... separiamoli quindi nell'esempio precedente (solo per motivi didattici):

"  \\  \\  \\  s*$"

Ora: anche nelle espressioni regolari il backslash ha una funziona speciale... serve anch'esso per fare l'escape di alcuni caratteri con un significato speciale come * (che significa da 0 a n), il + (che significa da 1 a n), ecc... Se volessimo quindi ricercare nelle espressioni regolari proprio il carattere * ad esempio, dovremmo scrivere \* ... e per ricercare il carattere backslash dovremmo scrivere \\ ...

Se avessimo una normale espressione regolare, per trovare un backslash alla fine di un testo avremmo:

\\$ e cioè: il carattere \ alla fine della riga (indicata nelle espressioni regolari dal carattere $)... in Java ricordiamoci che siamo dentro alle doppie virgolette (ricordate quanto detto prima? Ogni volta che intendiamo scrivere il carattere \ bisogna metterne due...). Questo si traduce in:

"\\\\$"

e ne abbiamo già ben 4. Se a questo volessimo anche rimuovere gli spazi al termine della riga (che nelle regex si scrive \s*$ e cioè tutti gli spazi di ogni tipo da 0 a n fino alla fine della riga), mettendo insieme con quanto fatto prima avremmo l'espressione: \\\s*$ che in Java con le doppie virgolette si traduce in: "\\\\\\s*$" ... Chiaro no? Mah...

Va beh, cambiamo scenario, ora pensate se volessimo ricercare con le Regex in Java se una parola è presente in fondo a un testo...

Con cose note è facile, ad esempio " ciao$" e l'espressione regolare è in grado di capire se l'ultima parola preceduta da uno spazio è ciao scritto in minuscolo...

Ora poniamo di avere la parola da ricercare in una variabile, ad esempio myWord ma non avere il controllo su di essa perchè inserita dall'utente in un passaggio precedente...

Voi direte: facile: " "+myWord+"$" (dove i + in questo caso servono come concatenazione di stringa)... e invece NO! Perchè myWord potrebbe contenere dei caratteri speciali che l'espressione regolare userebbe in modo appunto speciale; se ad esempio myWord = "la*matita" il risultato di quanto scritto sopra diventerebbe:

"la*matita$" quindi se il mio testo finisse con laaaaaaaaaaaaamatita diventerebbe una stringa accettata!

La prima cosa che viene in mente è creare una funzione che aggiusti myWord per farla digerire a replaceAll che piazzi dei backslash dove servono (e già qui comincia la nausea).

Bisognerebbe però creare l'algoritmo di questa funzione che, se trova un carattere speciale nella stringa, gli anteponga un backslash... magari usando dei search and replace proprio con delle espressioni regolari per fare le sostituzioni sulla stringa per ogni carattere... mettendo backslash... in pratica, un mezzo inferno di backslash!

Come ne usciamo?

Fortunatamente stavolta gli sviluppatori del linguaggio ci vengono incontro (secondo me hanno capito anche loro che serviva un aiuto in questo caso) e ci forniscono delle pratiche funzioni che fanno questa sostituzione per noi! GRAZIE <3 !

In pratica basta usare Pattern.quote nella parte di ricerca e Matcher.quoteReplacement nella parte di sostituzione (perchè non dimentichiamoci che anche in fase di sostituzione ci sono caratteri speciali come ad esempio $) e ci penserà direttamente Java a gestire gli escape per noi:

myText = myText.replaceAll(" "+Pattern.quote(myWord)+"$", " "+Matcher.quoteReplacement(mySubstitution));

Con buona pace di tutti quei backslash... che sicuramente però torneranno a trovarci nei nostri peggiori incubi!

Alla prossima

 

Una nota per i più curiosi / precisi: Pattern.quote, invece di riempire la stringa di backslash, nella realtà posiziona \Q all'inizio della stringa e \E alla fine per indicare all'espressione regolare di trattare ciò che c'è nel mezzo come caratteri e non con il loro significato speciale... Sicuramente più semplice da implementare per gli sviluppatori di Java e meno a rischio di errori che non piazzare backslash dappertutto ma, a prescindere dall'implementazione, l'importante è che non dobbiamo preoccuparcene noi ;)

Commenti

alessandro

Ogni volta che viene scritta una regular expression uno sviluppatore Java muore

Marco Brenna

In risposta a di alessandro

ahahaha cià Alessandro, alla fine il commento me l'hai scritto per davvero! Comunque con tutte quelle che ho scritto io nella mia vita, se veramente fosse così sarei complice di una strage :) Ci vediamo Martedi!

Aggiungi un commento

Solo ad uso interno per potervi rispondere, non verrà mostrato pubblicamente