Elaborazione file con Ant

mercoledì 23 maggio 2012 alle 23:13 | Pubblicato su Diario | 3 commenti

Se c’è una cosa che ho sempre trovato utile ma che non ho mai imparato ad usare molto bene sono le espressioni regolari. Non mi capita tutti i giorni ma regolarmente, magari due volte all’anno ho bisogno di fare operazioni su file testo in cui le espressioni regolari possono aiutare un sacco. Va precisato che sono uno di quelli che piuttosto che perdere 2 ore a fare un lavoro ripetitivo, impiega 4 ore per automatizzare quel lavoro (se posso), questo spiega perché mi affascinano le espressioni regolari. Poi nel lungo termine, con un po’ di fortuna quelle 2 ore di tempo investite si recuperano, se non altro perché il lavori manuali e ripetitivi sono soggetti a numerosi errori che con gli automatismi si evitano.

Se uno lavora su piattaforme *NIX non ha problemi a trovare strumenti che permettono di fare elaborazioni su file di testo con l’ausilio di espressioni regolari, il problema è imparare a usarli🙂

Io per lavoro utilizzo un pc con Windows, il quale di suo naturalmente non offre nulla che supporti le espressioni regolari, d’altra parte l’utente medio e sostenitore di windows non ragiona come me: preferisce percorrere decine di chilometri con il proprio mouse sulla scrivania e fare compiti ripetitivi piuttosto che studiare delle incomprensibili espressioni regolari. Siccome per la compilazione e preparazione dei pacchetti software utilizzo Ant ho avuto modo di apprendere alcune delle sue infinite potenzialità. Una che ho approfondito e utilizzato con soddisfazione è stata quella di elaborazione di file di testo basate su espressioni regolari. Come ho detto non mi capita spesso di dover fare queste operazioni,  immancabilmente quando mi capita mi sono scordato di come ho fatto la volta precedente, allora ho deciso di raccogliere qualche appunto qui su alcuni semplici task Ant che ho utilizzato.

Uno dei primi  “task” che ho provato è quello per la gestione dell’interattività con Ant stesso, il task  input


<!-- task per testare input e condition -->
 <target name="test_input" depends="" >
 <input
 message="which project? "
 addproperty="svn.project.name" />
 <condition property="full.project.name">
 <equals arg1="progetto_riferimento" arg2="${svn.project.name}" />
 </condition>
 <echo message="input: ${svn.project.name} - condition: ${full.project.name}" />
 </target>

Questo tipo di task è utile per rendere un po’ più flessibile l’uso di Ant stesso, rendendolo un minimo interattivo. In realtà sarebbe più comodo prelevare parametri di input direttamente dalla riga di comando, ad esempio:

ant task_1 parametro_1

ma a quel che so io il parametro “parametro_1” come l’ho scritto sopra viene interpretato da Ant come ulteriore task da eseguire. La soluzione sarebbe usare l’opzione -D<property>=<value> ma non mi piace moltissimo.

Nell’esempio sopra Ant stampa un messaggio di “prompt” (which project?)  e attende l’inserimento del parametro seguito da un invio, il parametro viene associato alla variabile svn.project.name che nel mio esempio viene poi usata nel task condition il quale testa se il valore della variabile è uguale alla stringa costante “progetto_riferimento”, se le stringhe sono uguali la variabile full.project.name viene valorizzata a true, altrimenti non viene valorizzata.

Una volta capito il task input l’ho potuto utilizzare per facilitarmi i test di altri task, in particolare del task  replaceregexp:


<!-- replaces all whitespaces (blanks, tabs, etc) by one blank remaining the line separator -->
 <target name="test_regexp">
 <input
 message="which project? "
 addproperty="my.file.name" />
 <replaceregexp match="\s+" replace=" " flags="g" byline="true">
 <fileset dir="" includes="${my.file.name}"/>
 </replaceregexp>
 </target>

Questo esempio è il primo che ho provato tempo fa, copiandolo dal manuale. In questo caso l’espressione regolare è “\s+” che indica una o più occorrenze (+) di un carattere di spaziatura (\s) e per ogni stringa che corrisponde (match) sostituisce l’occorrenza con un singolo spazio (replace=” “) Adesso passo a un task che soddisfa una delle esigenze che mi sono capitate:

<!-- task che toglie gli accapo / -->
 <target name="test_dropcr">
 <input
 message="file name? "
 addproperty="my.file.name" />
 <replaceregexp match="\n*\r*\s*" replace="" flags="g" byline="false">
 <fileset dir="" includes="${my.file.name}"/>
 </replaceregexp>
 </target>

Questo task toglie i caratteri di “newline” (\n), “carriage return” (\r) e anche tutti gli spazi (\s). In questo caso perchè il task funzioni occorre settare l’attributo  byline=”false”. Un’altro caso di task utile è legato al carattere terminatore per gli script per SQL*Plus, mi è capitato di aver necessità si sostiture il normale “;” a fine istruzione con il “/” a inizio riga successiva

<!-- task che sostituisce il ";" a fine riga con un accapo e / -->
 <target name="piazza_slash">
 <input
 message="file name? "
 addproperty="my.file.name" />
 <replaceregexp match="[;]{1}$" replace="${line.separator}/" flags="g" byline="true">
 <fileset dir="" includes="${my.file.name}"/>
 </replaceregexp>
 </target>

come ho scritto in altro “luogo” faccio ancora un po’ di confusione con parentesi quadre e tonde, ad esempio forse nell’esempio sopra si possono usare parentesi tonde, o forse non servono… Mi è capitato di voler/dover rimuovere un po’ di righe vuote (ad esempio quando mi mandano via mail script copiati da strumenti come toad me li trovo con una riga vuota per ogni riga di codice) in questo caso non ho trovato un’espressione regolare che mi aiutasse, però ho
trovato
il consiglio di utilizzare il task copycon il tag filterchain che per la verità non ho ben capito cos’è

<target name="remove_empty_lines">
 <input
 message="file name? "
 addproperty="my.file.name" />
 <copy file="${my.file.name}" tofile="mod_${my.file.name}">
 <filterchain>
 <tokenfilter>
 <ignoreblank/>
</tokenfilter>
 </filterchain>
 </copy>
 </target>
<pre>

Io poi ho esteso l’uso del task copy anche per fare una copia di backup del file prima di elaborarlo. Arrivo quindi all’ultimo tipo di elaborazione che ho provato:

<target name="remove_rem_lines">
 <input
 message="file name? "
 addproperty="my.file.name" />
 <copy file="${my.file.name}" tofile="orig_${my.file.name}">
 <filterchain>
 <tokenfilter>
 <!-- <ignoreblank/> -->
</tokenfilter>
 </filterchain>
 </copy>
 <replaceregexp match="^(REM ){1}(.)*" replace="" flags="g" byline="true">
 <fileset dir="" includes="${my.file.name}"/>
 </replaceregexp>
 <copy file="${my.file.name}" tofile="mod_${my.file.name}">
 <filterchain>
 <tokenfilter>
 <ignoreblank/>
</tokenfilter>
 </filterchain>
 </copy>
 </target>

In questo caso faccio prima una copia di backup del file mettendo il prefisso “orig_” nel nome, poi avevo necessità/voglia di eliminare da un file (prodotto dal’utility oracle imp) tutte le righe che iniziavano con “REM “. io qui faccio due passaggi, prima le svuoto con il task regexpreplace e l’espressione regolare “^(REM ){1}(.)*” e poi rimuovo le righe il task copy.
Sull’espressione “^(REM ){1}(.)*” ho avuto ed ho ancora qualche dubbio, inizialmente avevo scritto “^(REM ){1}[.]*” ma credo che non funzioni perché il “.” fra le parentesi quadre probabilmente viene interpretato letteralmente e non come espressione che indica un carattere qualunque. Funziona anche “^(REM ){1}.*” quindi senza le parentesi tonde che probabilmente in caso di singolo carattere come ne mio caso sono superflue.

3 commenti »

RSS feed for comments on this post. TrackBack URI

  1. Tu vuoi farti del male… usare Ant per elaborare files di testo…

    Comunque:
    ^(REM ){1}(.)*
    va bene per quello che intendi fare, ma io userei più semplicemente:
    ^REM .*
    invece:
    ^(REM ){1}[.]*
    non va bene, perché uno o più caratteri tra parentesi quadre rappresentano un carattere che può assumere solo i valori elencati all’interno delle quadre. La regular expression per me non è ben formata, e se non ti dà errore, chissà come la interpreta Ant. Forse, visto che non ci sono caratteri di escape “\”, le quadre le prende letteralmente e per esempio possibili righe sarebbero:
    REM [a
    REM [b]]]
    REM [c]]]]]]

    Io per le elaborazioni di files di testo (anche complesse) uso Perl, che oltre alla sua qualità migliore di permettere una rapidità di implementazione ineguagliabile, per chi lavora con Oracle (basta anche il client nel proprio pc), è sempre disponibile senza dover installare nulla anche in ambiente Windows.

    • fin’ora non mi sono fatto male🙂 Sono convinto che Perl sia lo strumento più adatto per questo tipo di elaborazioni, ma io sul pc ho instant client, niente Perl; ciò comunque non sarebbe certo il problema, è che cominciare a scrivere qualcosa di funzionante in Perl non so se è più facile per le mie esigenze, non ho mai avuto il coraggio e la voglia di provarci.
      Ant usa java, quindi non so in che modo l’implementazione delle espressioni regolari differisca da altre appunto perché io di espressioni regolari è chiaro che non ne so molto.
      Ho fatto ancora test con ^(REM ){1}[.]* ed Ant sembra praticamente ignorare la parte “[.]*” quindi corrisponde solo la parte “REM “, per intenderci delle stringhe
      line 1
      REM [
      –LINE COMMENT
      REM ciccio
      REM [.]
      REM [.]*
      REM .
      REM ..
      REM [a
      line 2

      sostituendo le corrispondenze di con nulla viene

      line 1
      [
      –LINE COMMENT
      ciccio
      [.]
      [.]*
      .
      ..
      [a
      line 2

      Se uso l’espressione ^(REM ){1}[a]* prende anche ad esempio la stringa REM a o REM aaa
      Quindi ne concludo che in ^(REM ){1}[.]* quello che non va bene è il “.” che non può essere usato dentro le quadre? Ho provato a usare l’escape, cioè ^(REM ){1}[\.]* ma non cambia

      • Chi prova il Perl non torna più indietro e non capisco perché dopo una partenza sfolgorante sia passato un po’ di moda.
        Riguardo l’instant client, facendo qualche esperimento in passato ricordo che per far funzionare Perl (il suo corpo principale che è gia molto ricco) bastano un paio di binari in PATH senza installazioni.

        Le espressioni regolari sono nate in ambiente UNIX, poi ci sono molte estensioni, e Perl (che originariamente era solo una super utility per elaborare file di testo) è un esempio forse tra i più estremi. Le sintassi di base comunque sono abbastanza semplici e sempre quelle.

        Le parentesi tonde se non servono non metterle, come hai già capito “REM ” equivale a “(REM ){1}”, le parentesi qui sono utili quando devi ripetere n volte una stringa (caso poco probabile), ad esempio “REM REM REM REM ” puoi scriverlo equivalentemente per brevità “(REM ){4}”. Credo dai tuoi esempi che nella sintassi di Ant, una stringa tra parentesi serva anche ad identificare cosa rimpiazzare con replaceregexp.
        Non so come si definisca il carattere di escape in Ant, deve sicuramente esistere, comunque l’espressione “[.]*” non è sensata e quindi non usarla, non puoi sapere con certezza come verrà interpretata. n (anche zero) caratteri qualsiasi, si indicano con “.*”, invece n caratteri appartenenti ad un sottinsieme si mettono dentro le parentesi quadre: “[abc123]*”, se c’è un carattere speciale come per esempio “.” o “*”, deve esserci un carattere di escape che ne annulla la specialità: “[abc\.12\*3]*” Ant avrà il suo modo di farlo (oppure semplicemente sceglie di considerare come normale ogni carattere all’interno delle parentesi quadre).


Lascia un commento

Inserisci i tuoi dati qui sotto o clicca su un'icona per effettuare l'accesso:

Logo WordPress.com

Stai commentando usando il tuo account WordPress.com. Chiudi sessione / Modifica )

Foto Twitter

Stai commentando usando il tuo account Twitter. Chiudi sessione / Modifica )

Foto di Facebook

Stai commentando usando il tuo account Facebook. Chiudi sessione / Modifica )

Google+ photo

Stai commentando usando il tuo account Google+. Chiudi sessione / Modifica )

Connessione a %s...

Blog su WordPress.com.
Entries e commenti feeds.

%d blogger cliccano Mi Piace per questo: