LibreOffice Writer/Trovare e sostituire: le espressioni regolari
Oltre alla barra degli strumenti di ricerca testo che viene attivata con Ctrl+F e che funziona più o meno come quella corrispondente nei navigatori internet, LibO offre un potente strumento per trovare testi complessi non solo in base al testo in sé, ma anche in funzione della sua formattazione e della sua struttura.
La finestra che si presenta con Ctrl+H o usando il menù Modifica → Trova e sostituisci ci offre, sotto Altre opzioni, strumenti per cercare per stili, per «simili» dove si permette al testo di essere leggermente diverso da quello ricercato (caratteri scambiati oppure aggiunti), uno strumento per cercare parole con un certo attributo o formattazione…
Di tutte queste opzioni qui parlerò soltanto di quella più «complicata», uno strumento potente, però un po' difficile da utilizzare: le espressioni regolari.
Cosa sono le espressioni regolari
modificaTutti conosciamo il famoso «asterisco» utilizzato per cercare un file determinato: scrivendo *.odt nello strumento per cercare file e cartelle del nostro sistema operativo si ottiene una lista di tutti i file odt. Qui l'asterisco funziona come un «jolly» che indica al sistema «sto cercando un file di qualsiasi nome, ma che finisca con l'estensione .odt».
Bene, le espressioni regolari sono un modo di portare avanti (fino all'infinito) questa idea di «jolly».
Immaginiamo di avere un testo nel quale ci sono numeri interi e che per qualsiasi motivo vogliamo selezionare il testo lasciando da parte i numeri, come si potrebbe fare questo?
Aprendo il quadro di dialogo «Cerca e sostituisci» si fa clic in Altre opzioni e si spunta Espressioni regolari. Adesso, in Trova dobbiamo scrivere quello che sarà la nostra prima espressione regolare:
(([^[0-9]]*)*)
Dovremo vedere qualcosa di simili a quello presentato nella Figura 75.
Se a questo punto facciamo clic in Trova successivo oppure in Trova tutto vedremo che il testo che non contiene numeri viene progressivamente selezionato.
Ebbene, adesso che sappiamo già per cosa servono le espressioni regolari andiamo avanti a vedere come vengono utilizzate.
Le espressioni regolari si possono avviare con Attributi e Formato ma non con «simili» o con la ricerca per stili.
I caratteri speciali
modificaDobbiamo dirlo dall'inizio: nelle espressioni regolari ci sono tanti «caratteri speciali» il cui significato cambia a seconda del contesto, il che non aiuta proprio a imparare il modo in cui vengono utilizzati.
Per esempio il carattere ^ da sé serve soltanto per localizzare elementi all'inizio di un paragrafo. Infatti, scrivendo
^un
in «Trova» il testo «un» verrà selezionato soltanto se si trova all'inizio di un paragrafo e sarà ignorato in altre posizioni. Ma se adesso mettiamo la medesima espressione tra parentesi quadre
[^un]
questo significherà «un carattere diverso da u e da n»: se nel testo si trova la parola «uno» e utilizziamo l'espressione regolare [^un] in «Trova», lo strumento ignorerà l'«un» e si fermerà nella «o», in «utilità» ignorerà la «u» e si fermerà nella «t»… Le parentesi quadre, oltre a essere utili per creare «negazioni» come quella precedente servono pure per indicare un «range» di caratteri. Per esempio
[0-7]
indica qualsiasi numero dallo zero al sette.
E qui l'espressione regolare con la quale si è aperto questo capitolo comincia ad avere un senso:
[^[0-9]]
rappresenta un carattere qualsiasi che non sia un numero.
Per altra parte l'asterisco viene utilizzato per rappresentare un numero arbitrario di caratteri uguali a quello precedente: a* troverà a, aa, aaa… eccetera, ma troverà anche zero caratteri: cal*do troverà caldo, calldo, calllllllllllllldo ma anche cado. Evidentemente, all'asterisco bisogna utilizzarlo con precauzione…
Sulle parentesi tonde parleremo più in giù.
A questo punto la nostra prima espressione regolare risulta chiara: cerca una quantità arbitraria di caratteri che non siano numeri. Andiamo a vedere altri esempi.
Il punto .
modificaPuò essere utilizzato come un carattere generico. Per esempio
m.s
cercherà per mas, más, mis… oppure mXs. Un'espressione equivalente al punto è
[:any:]
Il punto interrogativo ?
modificaServe per trovare «zero o una volta» il carattere precedente. Per esempio
vizio?
Troverà vizio e vizi.
Il più +
modificaSimilare all'asterisco, serve per indicare una o più volte il carattere precedente.
Le parentesi graffe { }
modificaServono a indicare quante ripetizioni ci interessano. Per esempio
a{1,4}y!
cercherà un minimo di una e un massimo di quattro ripetizioni della a e pertanto troverà ay!, aay!, aaay!, aaaay! Scrivendo soltanto un numero cercherà esattamente quel numero di ripetizioni, per esempio a{3}y! Troverà soltanto aaay! Per ultimo l'espressione a{3,}y! troverà la parola con almeno tre a, ma senza limite massimo.
Il carattere $
modificaCosì come ^ cerca all'inizio, il carattere $ preceduto da qualche testo cercherà quel testo alla fine di un paragrafo. Il carattere $ da solo può infatti essere utilizzato per cercare l'interruzione paragrafo ma risulta importante notare che non è possibile cercare un testo alla fine di un paragrafo più l'interruzione paragrafo. Per esempio l'espressione
\.$
troverà un punto alla fine di un paragrafo ma non selezionerà l'interruzione di paragrafo in sé.
Le espressioni regolari funzionano soltanto all'interno dei paragrafi. Nella casella «Sostituisci» il carattere $ ha un altro significato il quale vedremo più avanti al parlare dei gruppi e i riferimenti.
La barra inversa \
modificaQuesta barra può essere utilizzata per dire che caratteri con un significato speciale devono essere trattati come normali: se vogliamo trovare i caratteri . ^ $ * + ? \ [ ( { | dobbiamo mettere davanti la \ (giusto: per cercare \ dobbiamo scrivere \\).
Però questa barra può anche essere utilizzata per dire che dei caratteri normali hanno adesso un significato speciale: \b cerca nei «limiti» (boundaries, in inglese) di una parola. Per esempio
\bgius
troverà il «gius» di giusto, giusta, giustamente… ma lascerà passare quello di «aggiustare» mentre che
ora\b
troverà l'«ora» di mora, assapora… ma non quello di «orario».
Chiaramente i segni di punteggiatura non vengono considerati parte di una parola e perciò la espressione ora\b troverà anche l'«ora» di «proprio a quest'ora!».
E già che ci siamo qui a parlare di parole, l'espressione \w cercherà un elemento all'interno di una parola (un carattere).
\t cerca Tab ⇆. Dobbiamo tener presente che \tazza non cercherà la parola tazza ma un Tab ⇆ seguito da «azza» il che può risultare pericoloso in tanti modi.
\n ha un doppio significato: in Cerca andrà a trovare interruzioni di riga (⇧ Shift+↵ Invio), mentre che in Sostituisci introduce un'interruzione di paragrafo (esatto, quelle che si cercano con lo $… lo so, è strano…).
La barra verticale |
modificaUtilizzata tra parentesi quadre serve per «scegliere» tra due opzioni. Per esempio
bar[r|c]a
troverà barra e barca, ma non baria. Può essere utilizzato più di una volta in espressioni del tipo [a|b|c].
Se vogliamo cercare parole intere possiamo non utilizzare le parentesi quadre
primo|secondo|terzo
troverà le parole «primo», «secondo» o «terzo».
Gruppi e riferimenti
modificaLe parentesi tonde «raggruppano» espressioni. Il vantaggio principale è che le espressioni così raggruppate possono essere «richiamate » dopo. Vediamo un esempio. Supponiamo che vogliamo cercare un testo ripetuto (tipico errore tipografico che compare quando ci fermiamo a pensare a metà metà di una frase…) l'espressione regolare (più in giù ne vedremo una migliore)
(.+)\1
troverà qualsiasi gruppo di caratteri che si ripeta, per esempio due volte una parola o due volte uno spazio, perché il gruppo (.+) viene chiamato ancora con il \1.
NOTA: Se si hanno due o più gruppi, qualcosa del tipo (exp1)(exp2). potremo chiamare al primo con \1, al secondo con \2.
Per «pulire» questa ripetizione sarà sufficiente scrivere $1 in Sostituisci. E sì, Sostituisci funziona in modo diverso da Trova: il riferimento si fa con $ e non con \. Infatti, tranne per qualche eccezione molto particolare Sostituisci non accetta espressioni regolari. L'espressione precedente troverà anche due l consecutive, due r… insomma, che può risultare «pericolosa». Una espressione che da risultati perfetti per trovare parole duplicate è la seguente:
\b(\w+) +\1\b
Importante: notare lo spazio tra il (\w+) e il +\1
I due \b all'inizio e alla fine dell'espressione cercano i limiti di una parola, il (\w+) cerca almeno un elemento di parola (cioè, almeno un carattere). Lascio al lettore l'interpretare lo spazio seguito dal più…
Altre espressioni
modificaCosì come \w cerca un elemento di parola, \W (in maiuscolo) cerca qualcosa che non sia un elemento di parola (un segno di punteggiatura, uno spazio).
È possibile cercare caratteri unicode arbitrari utilizzando il formato «\uXXXX», dove XXXX corrisponde al codice esadecimale che identifica al carattere. Per esempio, \u03b4 troverà un δ.
L'espressione [:alpha:] rappresenta un carattere ASCII, mentre [:digit:] o [:number:] cercano un numero qualsiasi dal 0 al 9.
[:space:] troverà qualsiasi tipo si spazio, inclusi quelli di non separazione.
Scrivendo & in «sostituisci» verrà inserita la stessa catena trovata con l'espressione di «Trova».
Qualche esempio
modificaPer trovare paragrafi vuoti che contengano soltanto spazi ma non parole, caratteri o simboli possiamo scrivere
^([ ]*)$
(tra le parentesi quadre c'è uno spazio) in Trova. Per qualche motivo questa espressione non riesce a trovare il caso di «zero spazi», dove si deve utilizzare
^$
Per trovare numeri interi
\b[1-9][0-9]*\b
Se si vuole trovare un numero «con la virgola» del tipo 0,1234
\b[0-9]+,[0-9]*\b
E se dobbiamo trovare dei numeri che possano essere interi oppure decimali, con virgola o con punto come separatore decimale?
\b[0-9]+[,|\.]?[0-9]*\b
Vediamo adesso qualche esempio più difficile. Per trovare testo tra parentesi tondi:
\([^\(]*\)
La spiegazione di questa espressione è la seguente: cerca una parentesi di apertura, un numero arbitrario di caratteri che non siano una parentesi di apertura e infine un parentesi di chiusura.
Tanto le parentesi tonde come quelle quadre sono caratteri speciali per le espressioni regolari ed è per questo che si deve utilizzare la barra inversa, ma se vogliamo trovare per esempio qualcosa circondata nelle «virgolette latine» possiamo semplicemente utilizzare
«[^«]*»
Bene, siamo già in ritmo? Perché adesso vengono delle cose veramente complesse e interessanti…
Situazione: dobbiamo selezionare un testo che si trova prima di un altro, ma senza selezionare quest'ultimo. Risulta necessario utilizzare quello che nel gergo delle espressioni regolari si chiama Lookahead assertion e che si costruisce con un paio di parentesi tonde, un punto interrogativo e un segno uguale:
termine da selezionare(?=elemento che non vogliamo selezionare)
Esempio: dobbiamo selezionare qualsiasi testo immediatamente seguito da un punto, ma senza selezionare il punto, l'espressione sarebbe
\b\w+(?=\.)
Nuova situazione: prendiamo adesso la sfida di trovare qualcosa che venga dopo un elemento determinato, ma senza selezionare quell'elemento. La strategia viene chiamata Look-behind assertion la quale si costruisce in modo similare all'anteriore, ma aggiungendo in modo strategico con un segno di «minore che»:
(?<=elemento che non vogliamo selezionare) termine da selezionare
Esempio: cade nelle nostre mani un testo scritto da altre persone, possibilmente importato da altri formati, che ha più spazi dopo ogni punto, non solo uno, e vogliamo risolvere questo problema. L'espressione seguente sarebbe sufficiente
(?<=\.)[:space:]{2,}
Però cosa succede se questi spazi ripetuti si danno pure dopo un punto esclamativo o interrogativo? In questo caso l'espressione sarebbe
(?<=[\.\?\!])[:space:]{2,}
Altra sfida? Potremo cercare testo tra due virgolette ma senza selezionare le virgolette
(?<=«)[^»]+(?=»)
Un ultimo esempio: potremo utilizzare tutto questo per correggere la mancanza di uno spazio dopo un punto. Per esempio, se in un testo si tiene
Un periodo.Secondo periodo.Terzo periodo?Quarto. Il quinto è giusto.
E vogliamo selezionare soltanto i punti che non sono seguiti da uno spazio potremo utilizzare
(?<=\w)\.(?=[\w¿¡])
per poi sostituirle per un punto e uno spazio.
Credo che per una «introduzione» ci siamo già spinti fin troppo. Le espressioni regolari sono più un'arte che una tecnica e proprio per quello trovare «l'espressione giusta» per risolvere un dato problema è qualcosa che ci terrà a pensare per un bel po'.
Ma quando la troviamo…
Per più informazioni sulle espressioni regolari: http://userguide.icuproject.org/strings/regexp
Non tutte le espressioni che si trovano in quella pagina funzionano in Writer, ma risulta un importante punto di riferimento.