Espressioni regolari
Riassunto:
Le espressioni regolari sono utilizzate per ricerche avanzate sensibili al
contesto e revisioni di testo. Possono essere trovate in molti editor avanzati,
nei programmi e nei linguaggi di analisi.
Introduzione
Le espressioni regolari possono essere trovate in molti editor avanzati
come il vi, nei programmi grep/egrep e in linugaggi come l'awk, il perl e il
sed.
Le espressioni regolari sono utilizzate per ricerche avanzate sensibili
al contesto e per la revisione di testo. Un'espressione regolare è una descrizione formale di un modello che deve venir confrontato con una stringa di testo.
Quando, molti anni fa, vidi usare le espressioni regolari ne
rimasi affascinato. Revisioni di testo e ricerche che normalmente avrebbero
richiesto ore potevano essere svolte in pochi secondi. Eppure, non capii una
sola parola quando vidi l'espressione sullo schermo. Sembravano strane
combinazioni di punti, barre, stelle e altri caratteri. Ma ero determinato ad
imparare come funzionavano, e presto ci riuscii. Esse seguono delle semplici
regole di sintassi.
Anche se le espressioni regolari sono molte diffuse nel mondo Unix non
esiste nulla del tipo "il linguaggio standard delle espressioni regolari". E
più come se ci fossero molti dialetti diversi. Per esempio ci sono due tipi di
programmi grep; grep ed egrep. Entrambi usano le espressioni regolari, ma con
possibilità leggermente diverse. Il perl ha probabilmente la serie più
completa di espressioni regolari. Fortunatamente tutti seguono gli stessi
principii. Una volta compresa l'idea di base, è facile imparare i dettagli
delle singole varianti.
Questo articolo introdurrà le basi, potrete poi consultare le pagine dei
manuali dedicati ai singoli programmi per apprendere gli aspetti e le capacità
specifici di ciascuno.
Un semplice esempio
Diciamo che avete l'agenda telefonica di una compagnia, che appare così:
Phone Name ID
...
...
3412 Bob 123
3834 Jonny 333
1248 Kate 634
1423 Tony 567
2567 Peter 435
3567 Alice 535
1548 Kerry 534
...
E' una compagnia con 500 dipendenti. Tengono i dati in un semplice
file ascii. Le persone con 1 come prima cifra del numero di telefono lavorano
nell'edificio 1. Chi lavora nell'edificio 1?
Le espressioni regolari possono rispondere così:
grep '^1' phonelist.txt
or
egrep '^1' phonelist.txt
or
perl -ne 'print if (/^1/)' phonelist.txt
In parole semplici questo significa "cerca tutte le righe che iniziano
con un uno". Il segno "^" indica l'inizio di una riga. Costringe
l'intera espressione a rispondere solo se una linea ha un uno come primo
carattere.
Le regole di sintassi
Pattern a carattere singolo
La struttura base di un espressione regolare è il pattern a carattere
singolo. Ricerca solo questo carattere. Un esempio di pattern a carattere
singolo è l'1 nell'esempio precedente. Ricerca solo un 1 nel testo.
Un altro esempio di pattern a carattere singolo è: egrep 'Kerry' phonelist.txt
Questo pattern consiste solo di singoli caratteri (le lettere K,e ...)
I caratteri possono essere raccolti assieme in un set. Un set è
rappresentato da una parentesi aperta ed una chiusa e da una lista di
caratteri. Un set rappresenta in sè un unico carattere singolo. Uno ed uno solo
di questi caratteri deve essere presente nel testo analizzato per far reagire
il pattern. Per esempio:
[abc] E' un pattern a carattere singolo che riconosce le lettere a, b o c
[ab0-9] E' un pattern a carattere singolo che riconsce a, b o un numero
nelange ascii da zero a nove
[a-zA-Z0-9\-] Questo riconosce un singolo carattere che è o una
lettera maiuscola o minuscola, o un numero o il segno meno.
Proviamolo:
egrep '^1[348]' phonelist.txt
Questo ricerca le righe che iniziano con 13, 14 o 18.
Abbiamo già visto che alcuni caratteri ascii corrispondo solo a
quel carattere mentre altri hanno un significato particolare. Per esempio la
parentesi quadra inizia un set. Nel set "-" ha il significato
particolare di range. Per eliminare il significato particolare potete
precedere il caratter e con un backslash "\". Il segno meno in [a-zA-Z0-9\-] è
isun esempio di ciò. Ci sono anche dialetti del linguaggio regexp dovearatteri
speciali iniziano con un backslash. In questo caso per avere il significato
normale ocorre rimuovere il backslash.
Il punto è un carattere speciale importante. Riconosce tutto ad eccezione
del carattere di cambio riga. Per esempio:
grep '^.2' phonelist.txt
o
egrep '^.2' phonelist.txt
Questo ricerca le linee con un 2 in seconda posizione ed un qualsiasi
come primo carattere.
I set possono essere invertiti iniziandone la definizione con
"[^" invece che con "[". Il segno "^"
non significa più l'inizio della riga ma la combinazione di "[" e
"^" indica il set invertito.
[0-9] E' un patterna a carattere singolo che ricerca i numeri nel range
ascii da zero a nove.
[^0-9] Ricerca ogni carattere che non sia una cifra.
[^abc] Ricerca ogni carattere che non sia a, b o c.
. Il punto ricerca qualsiasi carattere fatta eccezione per il segno
di cambio riga.
E' lo stesso che [^\n]. Dove \n e' il carattere di cambio riga.
Per cercare tutte le righe che non iniziano con un 1 possiamo scrivere:
grep '^[^1]' phonelist.txt
o
egrep '^[^1]' phonelist.txt
Ancore
Già nella parte precedente abbiamo visto "^" che corrispondeva
all'inizio riga. Le ancore sono speciali caratteri che corrispondono a
posizioni nel testo e non a caratteri presenti nel testo.
^ Corrisponde all'inizio di una riga
$ Corrisponde alla fine di una riga
Per cercare una persona della compagnia con ID 567 nella nostra lista
phonelist.txt useremo:
egrep '567$' phonelist.txt
Questo ricerca le rige con 567 a fine riga.
Moltiplicatori
Un moltiplicatore determina quante volte un pattern a singolo carattere
deve essere presente nel testo.
descrizione | grep | egrep | perl | vi | vim<
/ t h > < th>vile | elvis | emacs |
zero o più volte | * | * | * | * | * | * | * |
* |
una o più volte | \{1,\} | + | + |
| \+ | \+ | \+ | + |
zero o una volta | \? | ? | ? |
| \= | \? | \= | ? |
da n a m volte | \{n,m\} |
| {n,m} | | |
| \{n,m\} | \{n,m\} |
Nota: I vari Vi hanno l'opzione magic settata per funzionare come mostrato
sopra.
Un esempio dall'agenda telefonica:
....
1248 Kate 634
....
1548 Kerry 534
....
Per trovare una riga che inizia con un 1, ha qualche cifra, almeno uno
spazio ed un nome che inizia per k possiamo scrivere:
grep '^1[0-9]\{1,\} \{1,\}K' phonelist.txt
o usare * e ripetere [0-9] e lo spazio:
grep '^1[0-9][0-9]* *K' phonelist.txt
o
egrep '^1[0-9]+ +K' phonelist.txt
o
perl -ne 'print if (/^1[0-9]+ +K/)' phonelist.txt
Il moltiplicatore moltiplica la presenza del pattern che lo precede.
Quindi "23*4" NON significa " 2 poi 3 e non 4" (Questo
sarebbe "23.*4"). Significa "una volta 2 poi forse molte volte
3 ed una 4"
E' anche importante notare che questi moltiplicatori sono avidi.
Questo significa che il primo moltiplicatore presente estende la su influenza
il più possibile.
L'espressione ^1.*4
troverebbe l'intera riga
1548 Kerry 534
dall'inizio fino all'ultimo 4.
Non riconosce il solo 154.
Questo non fa una gran differenza per il grep, ma è importante per le
reviisioni di testo e le sostituzioni.
L'uso delle parentesi come memoria
Le parentesi usate come memoria non cambiano il modo in cui un '
espressione riconosce il testo ma invece fanno memorizzare il testo incluso tra
esse, in modo che ci si possa riferire ad esso più avanti nell'espressione.
La parte memorizzata è disponibile attraverso variabili. Il primo blocco
memorizzato tra parentesi corrisponde alla variabile uno, il secondo alla due e
così via.
nome programma | sintassi delle parentesi | sintassile
variabili del |
grep | \(\) | \1 |
egrep | () | \1 |
perl | () | \1 o ${1} |
vi,vim,vile,elvis | \(\) | \1 |
emacs | \(\) | \1 |
Esempio:
L'espressione [a-z][a-z] riconscerà
due lettere minuscole.
Ora possiamo usare la variabile per ricercare pattern come 'otto':
egrep '([a-z])([a-z])\2\1'
La variabile \1 conteneva la lettera o
e la \2 la lettera t.
L'espressione riconoscerebbe anche il nome
anna ma non yxyx.
Le parentesi per la memorizzazione di blocchi non sono molto usate per la
ricerca di nomi come otto od anna, ma piuttosto per le revisioni e le
sostituzioni.
L'uso delle espressioni regolari per la revisione di testo
Per il lavoro di revisione avrete bisogno di un editor come il vi,cs,
oppure potete usare ,ad esempio, il perl.
In emacs usate M-x query-replace-regexp o potete assegnare il comando
query-replace-regexp command a. qualche tasto funzione. In alternativa potete
anche usare il comando replace-regexp. Il comando query-replace-regexp è
interattivo, l'altro no.
In vi si usa il comando di sostituzione :%s/ / /gc. La percentuale si
riferisce al range "tutto il file" e può essere sostituita da qualsiasi range
appropriato. Per esempio in vim digitate shift-v, segnate un area e poi usate
il comando di sostituzione solo in quell'area. Non spiego altro riguardo al
vimI perchè questo diventerebbe un tutorial autonomo a riguardo. Il comando
'go' è la versione interattiva. Quella non interattiva è s/ / /g
Interattivo significa che ad ogni ritrovamento vi viene chiesto
sefettuare o meno la sostituzione.
In perl potete usare
perl -pe 's/ / /g'
Vediamo un po' di esempi. Il modo di numerazione della nostra compagnia è
stato modificato, ed ad ogni numero che inizia con 1 viene aggiunto un 2 dopo
la seconda cifra. Questo significa che, ad esempio, 1423 diventerà 14223.
La vecchia lista:
Phone Name ID
...
3412 Bob 123
3834 Jonny 333
1248 Kate 634
1423 Tony 567
2567 Peter 435
3567 Alice 535
1548 Kerry 534
...
Ecco come effettuare la modifica:
vi: s/^\(1.\)/\12/g
emacs: ^\(1.\) sostituito da \12
perl: perl -pe 's/^(1.)/${1}2/g' phonelist.txt
Ora la nuova lista appare così:
Phone Name ID
...
3412 Bob 123
3834 Jonny 333
12248 Kate 634
14223 Tony 567
2567 Peter 435
3567 Alice 535
15248 Kerry 534
...
Il perl può gestire più variabili rispetto a quelle da \1 a \9, quindi
\12 si riferirebbe alla dodicesima variabile, che naturalmente è vuota. Per
risolvere questo problema usiamo ${1}.
Ora l'allineamento nella lista è stato alterato. Come si può
risolverequesto problema? Potete semplicemente verificare se vi è uno
spazio bianco in quinta posizione ed inserirne un altro:
vi: s/^\(....\) /\1 /g
emacs: '^\(....\) ' sostituito da '\1 '
perl: perl -pe 's/^(....) /${1} /g' phonelist.txt
Ora la lista appare così:
Phone Name ID
...
3412 Bob 123
3834 Jonny 333
12248 Kate 634
14223 Tony 567
2567 Peter 435
3567 Alice 535
15248 Kerry 534
...
Un collega ha editato la lista manualmente e accidentalmente ha inserito
qualche spazio all'inizio di alcune righe. Come possiamo eliminarli?
Phone Name ID
...
3412 Bob 123
3834 Jonny 333
12248 Kate 634
14223 Tony 567
2567 Peter 435
3567 Alice 535
15248 Kerry 534
...
Questo dovrebbe rimuoverli:
vi: s/^ *// (C'è un secondo spazio perchè non abbiamo un +)
emacs: '^ +' sostituito dalla riga vuota
perl: perl -pe 's/^ +//' phonelist.txt
State scrivendo un programma ed avete le variabili temp e temporary. Ora
vorreste sostituire la variabile temp con la variabile name counter. Se la
stringa temp è semplicemente rimpiazzata con counter temporary diverrebbe
counterorary, e non è ciò che volete.
Le espressioni regolari possono farlo. Semplicemente rimpiazzate
temp([^o]) con counter\1. Questo significa su temp e non sulla lettera o.
(Un'alternativa sarebbe usare i boundaries, ma non abbiamo discusso di questo
tipo di pattern per l'authoring.)
Spero che questo articolo abbia catturato la vostra attenzione. Ora
potete cercare le man-page del vostro editor preferito ed apprendere i
dettagli.
Ci sono anche molti altri caratteri specifici, come ad esempio
l'alterazione, che è una specie di "o" e anche i boundaries
menzionati prima.
Divertitevi, buon editing.
|