Robitex's Blog

Ideas in the web

Riallineare valori testuali


Dati testuali tabellari

A volte abbiamo la necessità di dover riallineare un testo tabellare inserendo o cancellando caratteri spazio. In questo modo i valori delle celle risulteranno allineati in colonne se stampati con un font a spaziatura fissa tipo macchina da scrivere.

Per intenderci, da così:

123 89.08 33.9 5.90 67.5
8 78.00 4944.9 208.93
567 8.99 99.2 90.89 7890.2
41 18.03 5464.6 38.73 993.3

a così:

123 89.08   33.9   5.90   67.5
  8 78.00 4944.9 208.93
567  8.99   99.2  90.89 7890.2
 41 18.03 5464.6  38.73  993.3

È il caso in cui un qualche programma fornisce in output dei file testuali di dati che dobbiamo impaginare per la stampa, oppure, quando è necessario estrarre dati da un file testuale in cui i campi devono essere individuati con la posizione dei caratteri (il cosiddetto tracciato record).

Naturalmente si può fare a mano con un buon editor di testi (che magari facilita il compito permettendo la selezione di aree rettangolari di testo come si puà fare con SciTE), ma se le linee sono migliaia…

Impaginare a mano grandi moli di dati non è solo un compito impossibile e soggetto ad inevitabili errori. In fondo anche le normative sulla sicurezza sul lavoro (come il nostro Testo Unico 81/08) impongono di fare tutto il possibile per evitare compiti ripetitivi e noiosi.

La soluzione

Un programma può svolgere egregiamente questo compito, ed in passato, chissà quante volte ne ho rimpianto la mancanza. Ma adesso che son cresciuto, vi propongo una soluzione grazie a Lua, il linguaggio di scripting più potente della storia!!!

Vai con il codice

Per la parte divertente ci riferiremo dunque a Lua. Lascio a voi il compito eventuale di installare Lua versione 5.1 o successiva sul vostro sistema (su Linux spesso lo trovate già pronto).

#!/usr/bin/lua
-- read the text file line by line
-- and reformatting the spacing about numbers

-- if no output filename is available, one is provided
local filename = arg[1]
local filenameResult = arg[2]

if not arg[2] then
   local i, j, ext = string.find( filename , "%.([^%.]*)$" )
   if ext then
      filenameResult = string.sub(filename, 1, i - 1) .. "_result." .. ext
   else 
      filenameResult = filename .. "_result"
   end
end

local f = assert(io.open( filename , "r" ))
local words = {}
local coldim = {}

while true do
   local line = f:read("*line")
   if line == nil then break end

   j = 0
   k = 1      -- a words counter in a line
   t = {}
   while true do
      i,j = string.find(line, "%S+", j+1)
      if i == nil then break end
      
      -- a word must be processed
      local w = string.sub(line, i, j )
      t[ k ] = w
      local dim = #w
      if coldim[k] == nil then
          coldim[k] = dim
      elseif coldim[k] < dim then
          coldim[k] = dim
      end
      k = k + 1
   end
   words[ #words + 1 ] = t
end

f:close()

local f = assert(io.open( filenameResult ,"w"))

for i,v in ipairs( words ) do
   local rebuildedLine = ''
   for j,w in ipairs( v ) do
      rebuildedLine = rebuildedLine ..
         string.rep(" ", coldim[j]-#w) ..
            ((j>1) and ' ' or '' ) .. w
   end
   f:write(rebuildedLine .. '\n')
end

f:close()
-- end

Il codice si sviluppa dapprima memorizzando nella tabella words le tabelle v contenenti le “parole” (sequenze di caratteri non contenenti spazi). La ricerca delle parole viene risolta utilizzando una funzione della libreria di Lua chiamata string.find che accetta un pattern di ricerca (il pattern “%S” con la s maiuscola, indica un carattere diverso da un blanck, “%S+” indica uno o più caratteri diversi da blanck).

Così elaborato l’intero file, si procede a ricostruire il file risultato ordinatamente linea per linea eseguendo un doppio ciclo for sulla tabella di tabelle. La funzione string.rep (sempre della libreria di Lua), restituisce una stringa ripetendo quella di input per il numero di volte voluto. Quindi è possibile “giustificare” a destra le parole anteponendovi il giusto numero di spazi.

Si ma come si fa a sapere “il giusto numero di spazi”? In un ulteriore tabella memorizzata in coldim viene memorizzato colonna per colonna la massima lunghezza della parola.

Ulteriore notizia riguarda l’operatore # disponibile dalla versione 5.1 di Lua, che restituisce la dimensione dell’oggetto che segue, quindi la lunghezza di una stringa od le dimensioni di un array (una tabella indicizzata con i numeri interi).

Ricordiamoci che in Lua l’unica struttura dati disponibile è la tabella, ovvero un array associativo dove la chiave può essere un numero od una stringa. Per instanziare un oggetto tabella l’unico modo è ricorrere al costrutture che nella forma più semplice è questo {} che crea una tabella vuota e ne restituisce il riferimento che possiamo assegnare ad una variabile.

Se volete è certamente consigliabile studiarsi il libro “Programming in Lua”, citato anche in questo mio post.

Via al codice

Per lanciare lo script salvare il codice in un file con il nome ftab.lua (renderlo eseguibile se nessario), aprire un terminale e digitare il comando:

./ftab.lua nomedelfileditesto
oppure
lua ftab.lua nomedelfileditesto

Bene. Se vi ho risparmiato un mese di lavoro fatemelo sapere!!! Vi manderò il conto.

Ps. naturalmente il codice può essere esteso, per esempio lasciando scegliere all’utente il numero di spazi bianchi tra le colonne del file di output, oppure giustificando “a destra” i numeri ed “a sinistra” le stringhe, od ancora predisporlo per l’elaborazione di file CSV (Comma Separated Value), o per riallineare ambienti tabular in un sorgente LaTeX.
Lua è tremendamente potente.

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

%d blogger cliccano Mi Piace per questo: