Manipolazione di testi: espressioni regolari

Transcript

Manipolazione di testi: espressioni regolari
Manipolazione di testi: espressioni regolari
• Un meccanismo per specificare un pattern, che, di fatto, è la
rappresentazione sintetica di un insieme (eventualmente infinito)
di stringhe: il pattern viene detto espressione regolare (regexp)
• Una stringa soddisfa il pattern (ovvero è selezionata dal processo
di “pattern matching”) se è inclusa nell’insieme rappresentato dal
pattern
• Le espressioni regolari hanno somiglianze con i meccanismi di file
expansion ma NON SONO LA STESSA COSA e adottano
CONVENZIONI DIVERSE anche sulle SCELTE BASILARI
(p.e. il simbolo corrispondente al carattere jolly)
• Quindi si usano in CONTESTI DIVERSI, in particolare
UN’ESPRESSIONE REGOLARE IN UNA LINEA DI
COMANDO VA QUOTATA, A SCANSO DI EQUIVOCI
Primi passi
• Ovviamente le regexp sono case sensitive
• I due primi simboli fondamentali sono . e *
• Il carattere . indica qualunque singolo carattere, quindi:
A.C matcha con: AAC, ABC, AbC, A C, A+C, A.C,...
• Il carattere * indica un numero da 0 a qualunque ripetizioni del
carattere che precede, quindi:
A* matcha con: stringa nulla, A, AA, AAA, AAAA, ...
AA* matcha con: A, AA, AAA, AAAA, ...
BA* matcha con: B, BA, BAA, BAAA, BAAAA, ...
A.*O matcha con: AO, AIUTO, ADESSO CI SONO,
AFFANNO, AFFAN...O, ...
• N.B. Gli esempi sopra sono semplificati, come si vedrà meglio in
seguito il matching non si effettua solo all’inizio della stringa
Anchors
• I caratteri ^ e $ sono anchors che specificano la posizione
all’interno della stringa
• ^ se è il primo carattere del pattern, indica l’inizio della stringa:
^From indica tutte le stringhe che iniziano con From
• $ se è l’ultimo carattere del pattern, indica la fine della stringa: :
Ciao$ indica tutte le stringhe che finiscono con Ciao
^.$ indica tutte le stringhe composte da un solo carattere
\.$ indica tutte le stringhe che finiscono col carattere .
Insiemi di caratteri
• [ ] specificano un insieme di caratteri, il matching è soddisfatto da
qualunque carattere nell’insieme, il trattino indica un range:
[abc]
un carattere tra a b c
[aeiou] qualunque vocale minuscola
[0-9]
qualunque carattere numerico
[A-Z]
qualunque lettera maiuscola
[a-zA-Z]qualunque carattere alfabetico
• Si può indicare il complemento di un insieme specificando come
primo carattere ^
[^0-9] qualunque carattere non numerico
[^zZ]
qualunque carattere che non sia una zeta
• Esempio: ^T[a-z][aeiou] qualunque stringa che inizia con
T, seguito da un carattere minuscolo seguito da una vocale
Ripetizioni
• Combinando insiemi e ripetizioni si possono ottenere definizioni
articolate:
[0-9]*
qualunque sequenza di cifre (anche vuota)
[0-9] [0-9]* qualunque sequenza di cifre non vuota
[^a-zA-Z]*
qualunque sequenza (anche vuota) di caratteri
non alfabetici
• Si può specificare l’intervallo nel quale deve cadere il numero di
ripetizioni desiderate con \{numeromin, numeromax \}:
^A\{3,5\} una stringa che inizia con 3, 4 o 5 A
• Si può omettere il limite superiore o specificare un singolo valore:
[A-Z]\{3,\} qualunque sequenza di almeno 3 maiuscole
[0-9]\{4\}
una sequenza di esattamente 4 cifre
Parole isolate
• In generale il matching si effettua sul contenuto della stringa in
modo indiscriminato: il pattern oro matcha con oro, foro, tesoro,
oroscopo, corollario ...
• Non è banale caratterizzare una parola isolata (spaziatori, segni
d’interpunzione, fine linea, caratteri speciali ...)
• Con \< \> si specifica che il matching deve avvenire su una
parola isolata (preceduta e seguita da un fine linea, o da un
carattere che non sia alfanumerico nè underscore)
\<[Tt]he\> qualunque apparizione dell’articolo the (o The)
La vispa Teresa
•
•
•
•
•
•
Cercare tutte le righe dove compare Teresa
Cercare tutte le righe dove compare una h
Tutte le righe che iniziano con a
Tutte le righe che iniziano con a isolata
Tutte le righe dove c'e' una parola che finisce con "esa "
Varianti sulla presenza di “ale”: in qualunque posizione, a fine
riga, come parola isolata
• Varianti sulla presenza di “ndo”: in qualunque posizione, a fine
riga, come fine parola
Memorizzare sezioni di pattern
• Se all’interno di un pattern se ne racchiude una parte tra \( \) si
“memorizza” quella parte per poterla richiamare successivamente
• Si possono memorizzare fino a 9 parti, che sarano richiamabili
ciascuna riferendosi al numero d’ordine di memorizzazione:
\1 per la prima parte memorizzata,\2 per la seconda ...
• Es. cercare sequenze di due caratteri minuscoli uguali:
\([a-z])\1
• Per la pagina della Sfinge, cosa specifica la seguente regexp ?
\([a-z]\)\([a-z]\)[a-z]\2\1
Una parola palindroma minuscola di 5 lettere
Estensione del pattern matching
• Va ricordato che il pattern matching viene effettuato sulla
massima estensione possibile all’interno della stringa esaminata
• In alcuni casi questo può dare luogo a risultati non
immediatamente intuitivi.
• Per la pagina della Sfinge: se applico \(".*"\) alla stringa
Disse: "Sì",poi corresse "No"
cosa contiene \1 ?
"Sì",poi corresse "No"
• Se invece applico \("[^"]*"\) alla stessa stringa cosa
contiene \1 ?
"Sì"
• Perchè ?
Regexp con sintassi estesa (extended regexp)
• Le regexp come sopra descritte sono utilizzate da molti
programmi: grep, vi, more, less, sed, ..
• Alcuni programmi (egrep, awk, ...) utilizzano alcune comode
estensioni della sintassi:
? indica 0 o una istanza di ciò che precede
+ indica una o piu' istanze di ciò che precede
(pat1|pat2) indica l’or tra due pattern
• Giusto per compensare, le extended regexp non hanno la sintassi
\< \> per le parole isolate
Esempi vari per divertirsi
•
Month Day, Year (JAN 05, 2003) o (January 05, 2004)
[A-Z][A-Za-z]\{2,8\} [0-9] \{1,2\}, [0-9] \{4\}
•
Codice fiscale (BBBPPP77R13G157K)
[A-Z] \{6\} [0-9] \{2\} [A-Z] \{1\} [0-9] \{2\} [A-Z] \{1\} [0-9] \{3\} [A-Z] \{1\}
•
Cifra in dollari ($1) o ($ 123456.78) in formato con extended regexp
\$ *[0-9]+(\.[0-9][0-9])?
•
Una linea vuota
^$
Un’intera linea
^.*$
Uno o più spazi
*
•
•
Line editors
• Gli editor di linea sono programmi che operano (tipicamente) su
flussi di testo (file o pipe) in modalità non interattiva eseguendo
operazioni su ciascuna linea del flusso individualmente
• Per quanto inusuale, l’uso di questo tipo di strumenti può essere
utile in molti casi nell’ambito di programmi script
• Questo tipo di programmazione non brilla per leggibilità nè per
facilità di sviluppo: si consiglia di procedere per piccoli passi,
verificando con cura i progressivi arricchimenti di funzionalità che
vengono introdotti
• Considereremo l’editor di linea denominato sed,oltre ai più
semplici tr e cut
• Per chi volesse andare oltre: awk
sed: schema di funzionamento
• sed riceve un flusso in ingresso, applica dei comandi riga per
riga, e produce in uscita il flusso risultante: quindi sed non
modifica i dati in ingresso ma produce un nuovo flusso separato
• sed per default opera su ciascuna riga del flusso in ingresso,
riportandola pari pari in uscita, se alla riga non si applica nessuno
dei comandi specificati
• sed -n inverte la modalità di default: solo le righe alle quali si
applica un comando vengono prodotte in output
• sed -f nomefilecomandi : i comandi da eseguire vengono
letti da nomefilecomandi anzichè essere specificati sulla linea
di comando stessa (vedi lucido successivo)
sed: sintassi di invocazione
sed 'comandosed' nomefileinput
viene specificato un singolo comando sed da applicare al file di
input, se il nomefileinput non è presente si applica allo
standard input. Di norma un comando è protetto da quoting forte.
sed 'comando1' -e 'comando2' ... nomefileinput
i due (o più ...) comandi sed da applicare al file di input vengono
eseguiti su ciascuna riga dell’input (tutti i comandi sulla prima
riga dell’input, poi tutti sulla seconda)
sed 'comando1
comando2
...' nomefileinput
come sopra con comandi scritti su più righe all’interno di un unica
sequenza protetta da quoting
Come è fatto un comando sed
address action
• L’idea base è che un comando indica un azione da applicare a tutte
e sole le righe del flusso di input che soddisfano l’address
• Se l’address non è specificato, l’azione viene applicata a tutte le
righe del flusso di input
• Nella scrittura del comando le definizioni di address e action sono
in realtà contigue (non sono separate da spazio o altri separatori)
address1,address2 action
• Come sopra, con azione da applicare al range di tutte e sole le
righe che si trovano tra la prima riga che soddisfa address1 e
la prima riga successiva che soddisfa address2
• In entrambi i casi, un ! tra address (o range) e action indica la
negazione: l’azione viene applicata alle righe complementari
Line addressing
• I più comuni tipi di address di linea sono per numero e per pattern
• Indicazione del numero di linea ($ indica l’ultima):
1
la prima linea del flusso
3,5
la terza, quarta e quinta linea
• Specifica di un pattern
/regexp/
tutte le linee che soddisfano il matching
con regexp
/begin/,/end/ tutte le linee incluse tra una riga che
contiene begin e una riga che contiene end
/begin/,/end/! tutte le linee NON incluse tra una riga che
contiene begin e una riga che contiene end
• Sono possibili range con specifica “mista”: p.e. (/begin/,$
oppure 1,/Subject/ oppure …
Comandi sed: delete
• La sintassi dei comandi sed è sintetica (un singolo carattere per
comando)
• Il comando più semplice è d: sopprime dall’output le righe alle
quali viene applicato
• Giocherellare con d può servire a capire meglio il meccanismo
generale
sed 1d
sopprime la prima riga
sed '1!d'
sopprime tutte le righe tranne la prima
sed '/^#/d' sopprime tutte le righe che iniziano con #
Comandi sed: sostituzione di pattern
• Il comando di sostituzione è probabilmente il più frequente
s/search/replacement/
sostituisce in ogni riga (che soddisfa address se specificato) LA
PRIMA ISTANZA di search con replacement
s/search/replacement/g
sostituisce in ogni riga (che soddisfa address se specificato) OGNI
ISTANZA di search con replacement
• Il ritorno della vispa Teresa:
- sostituire Teresa con un’altro nome
- sostituire il suffisso etta con accia
Il ritorno della Sfinge
s/search/replacement/
• search e replacement sono regexp, quindi replacement può
contenere:
- riferimenti a eventuali parti memorizzate in search (es. \1, \2)
- un riferimento all’intero pattern che corrisponde a search
(indicato dal carattere &)
• Torna la Sfinge: cosa fa il seguente comando ?
sed 's/#.*/\
&/'
Mette un newline prima di ogni inizio commento in uno script
La vendetta della Sfinge
• Come modificare il comando precedente in modo che non si metta
un newline prima delle righe che già iniziano con # ?
• Banalmente:
sed 's/\(^[^#][^#]*\)\(#.*\)/\1\
\2/'
• Dopo il necessario digestivo, a voi la scelta quando si tratta di
problemi che richiedano “semplici” elaborazioni su dati testuali:
- sorbirsi pazientemente le osticità di sed, sfruttandone però la
potenza
- scriversi (banalmente) l’equivalente programma in C, in Java o
nel linguaggio preferito
- non contenti di sed, avventurarsi verso altri linguaggi di simile
impostazione ma più complessi e potenti quali awk o perl
Altri comandi sed (non tutti)
• append: aggiunge una nuova riga con un certo testo dopo ciascuna
riga che soddisfa l’address
a\
testodaaggiungere
• insert: aggiunge una nuova riga con un certo testo prima di
ciascuna riga che soddisfa l’address
i\
testodaaggiungere
• sostituzione su address:
se address singolo, sostituisce ciascuna delle righe che soddisfano
l’address con un certo testo; se address espresso come range,
sostituisce l’intero range con il testo specificato
c\
testosostitutivo
Sostituzioni di caratteri: tr
• Il comando tr effettua sostituzioni o eliminazioni di caratteri su un
flusso in input inviandone il risultato sullo standard output
• Uso per sostituzioni:
tr setchar1 setchar2
ogni carattere di setchar1 viene sostituito dal carattere
corrispondente per posizione all’interno di setchar2
• I due set devono avere la stessa cardinalità, si estende set2 col suo
ultimo char se troppo corto o lo si tronca se troppo lungo
• Esempi:
tr "a-z" "A-Z" converte le minuscole in maiuscole
tr "A-Z" "a-z" converte le maiuscole in minuscole
tr "123" "ABC" sostituisce 1 con A, 2 con B ...
tr ”0-9" "#"
sostituisce qualunque numero con #
Altri usi di tr
tr -d setchar1
Elimina tutti i caratteri inclusi nel setchar1
Esempio:
tr -d ",.;:!?" per eliminare la punteggiatura
tr -s setchar1
Elimina tutte le ripetizioni dei caratteri inclusi nel setchar1
sostituendole con una loro istanza singola
Esempio:
tr -s " "
per eliminare gli spazi ripetuti
Comando cut
• Il comando cut serve a selezionare una parte di ogni riga di un
flusso di testo e a inviarla sullo standard output
• Le righe si considerano suddivise in fields separati da tab o da
altro separatore specificato con l’opzione -d
• L’opzione -f serve a specificare quali fields (uno o un range)
devono essere riprodotti sullo standard output
• Esempi:
who | cut -d" " -f1
visualizza solo i nomi degli utenti attualmente collegati
ps | tr -s " "|cut -d" " -f2
visualizza solo la colonna dei PID correnti
ps | tr -s " "|cut -d" " -f4-5 | sed 1d
visualizza solo le colonne TIME e CMD senza l’intestazione
No comment
• Scrivere uno script di shell che serve ad eliminare le righe vuote e
i commenti da un altro script di shell inviando in output solo le
parti “utili”:
- si definisce riga vuota una riga che contiene solo spazi o nessun
carattere (facoltativamente e SUCCESSIVAMENTE considerare
anche il caso in cui si considerino anche caratteri tab)
- le righe interamente commentate vanno eliminate
- delle righe commentate parzialmente si deve mantenere la parte
che precede il primo carattere # a meno che non sia costituita
interamente da caratteri spazio (e facoltativamente tab)
• Provare il programma su un file di test appositamente predisposto
No comment 2: ...
• Provare ad impostare/sviluppare uno script per l’eliminazione di
righe vuote e commenti anche per programmi C o Java
• La parte di commenti fino a fine riga è analoga alla precedente
solo che iniziano con //
• Non è complicatissimo estendere quanto visto in precedenza per il
caso dei commenti su più linee /* ... */
• Chi va piano, va sano e va lontano: procedere con un comando sed
per volta isolando e separando …
• … una volta isolate le righe con /* e */ si può fare
sed '/\/\*/,/\*\//d'