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'