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.
|