Home Map Index Search News Archives Links About LF
[Top Bar]
[Bottom Bar]
[Photo of the Author]
Guido Socher
L'Autore: Ama il linux perchè è un sistema libero e anche perchè è molto piacevole lavorare con la gente della comunità Linux di tutto il mondo. Nel tempo libero sta con la propria ragazza, ascolta alla radio la BBC World Service, gira in bicicletta per la campagna e si diverte a giocare con Linux.

Contenuto:
Introduzione
Un semplice esempio
Le regole di sintassi
L'uso delle espressioni regolari per il text editing

Espressioni regolari

[Illustration]

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.

descrizionegrepegrepperlvivim< / t h > < th>vileelvisemacs
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 programmasintassi delle parentesisintassile 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.



Traduzione in italiano a cura di Seipse

Questo sito e' gestito da Miguel Angel Sepulveda
© Guido Socher 1998
LinuxFocus 1998