OrdinareUnFile
Ordinare un file e’ un operazione banale, e di norma non avremo bisogno di usare ruby
per farlo, visto che i sono gia’ piccoli programmini che lo fanno (il classico sort di
unix, ad esempio).
Eppure e’ utile vedere come farlo con ruby, per aiutare a capire come manipolare i file
e come usare i metodi di ordinamento degli oggetti Enumerable
Il metodo piu’ classico e’ questo:
file=File.new('tmp.txt')
linee=file.readlines
file.close
linee.sort!
file2 = File.new('tmp2.txt','w+')
for linea in linee
file2.write(linea)
end
file2.close
Che tradotto significa:
(1) apri il file tmp.txt
(2) metti tutte le linee del file in un array, di nome linee
(3) chiudi il file tmp.txt che non serve piu’
(4) ordina l’array in place cioe’ senza crearne un altro
(5) apri un nuovo file
(6) per ogni elemento dell’array, scrivilo nel file tmp2.txt
(7) chiudi tmp2.txt
Questa soluzione e’ semplice, ma un po’ incompleta e un po’ troppo lunga.
Semplifichiamo. Anzitutto possiamo usare open() invece che File.new, il
risultato non cambiera’:
file=open('tmp.txt')
poi, potremmo spostare l’operazione di sort nello stesso punto linea in cui
leggiamo le linee:
linee=file.readlines.sort!
ed in effetti, non abbiamo bisogno di sort!. Infatti questo metodo serve a modificare
un array in place cioe’ senza crearne un altro. Ma a noi l’array non ordinato
non serve, ci basta assegnare a linee direttamente quello ordinato:
linee=file.readlines.sort
Il codice dunque, a questo punto sara’ cosi’ :
file=open('tmp.txt')
linee=file.readlines.sort
file.close
file2 = open('tmp2.txt','w+')
for linea in linee
file2.write(linea)
end
file2.close
ma quel for... occupa addirittura tre linee, troppa roba.
A questo punto ci torna utile ri. infatti, guardate qui:
ri IO.puts
—-—-—-—-—-—-—-—-—-—-—-—-— IO#puts
ios.puts( [anObject]* ) -> nil
Writes the given objects to ios as with IO#print. Writes a record separator (typically a newline) after any that do not already end with a newline sequence. If called with an array argument, writes each element on a new line.
Notate il dettaglio? Esatto, non ci serve a niente iterare, ci bastera’ passare l’array a puts per vedercelo stampare tutto, un elemento per ogni linea.
Dunque possiamo scrivere:
file=open('tmp.txt')
linee=file.readlines.sort
file.close
file2 = open('tmp2.txt','w+')
file2.puts(linee)
file2.close
Mh.. si, va gia’ molto meglio. Ma ora notate un altra cosa carina:
un oggetto di tipo File include il modulo Enumerable.
Questo significa che possiamo usare sort() direttamente su quell’oggetto,
senza leggere l’array con readlines. Inoltre, in realta’ non
abbiamo bisogno di usare file e file2:
linee=open('tmp.txt').sort
open('tmp2.txt','w+').puts(linee)
Eh si, avete ragione, anche linee non serve a niente:
open(‘tmp.txt’,’w+’).puts open(‘tmp.txt’).sort
In effetti, cmq, questa soluzione e’ scorretta. Manca una gestione sensata degli errori.
Gestire gli errori e’ importante. Un metodo classico per farlo e’ racchiudere il codice
in un blocco begin..end:
begin
file=open(‘tmp.txt’)
linee=file.readlines.sort
file2 = open(‘tmp2.txt’,’w+’)
file2.puts(linee)
rescue
puts “avvenuta eccezione!!”
ensure
file.close
file2.close
end
Come vedete, in questo esempio ci preoccupiamo di chiudere i file in ogni caso, e di comunicare evantuali errori all’utente. Avremmo potuto anche racchiudere il tutto in un metodo:
def ordina(filein,fileout)
file=open(filein)
linee=file.readlines.sort
file2 = open(fileout,'w+')
file2.puts(linee)
rescue
puts "avvenuta eccezione!!"
ensure
file.close
file2.close
end
notate come non ci sia bisogno di definire un blocco begin..end, in quanto ruby
capisce da solo che il blocco che ci interessa e’ quello del def..end.
Ma se vogliamo davvero una soluzione ottimale, faremo cosi’:
open('tmp2.txt','w+') do |fd|
fd.puts open('tmp.txt').sort
end
In questa ultima soluzione gli errori vengono gestiti direttamente da open(),
non lasciamo nessun descrittore di file aperto. Infatti sara’ la open stessa, alla fine del blocco, a preoccuparsi di richiamare il GC e ripulire tutto.
Provate a farlo con un altro linguaggio in maniera altrattanto pulita e chiara :)