Leggere byte per byte
Capita di dover leggere un file non di testo, come p.es. un file musicale o un’immagine o un eseguibile o uno zip compresso o roba simile. In questo caso occorre leggere tutti i singoli byte. Ecco come leggere il file testo.txt
, questo:
Questo lo leggo
byte per byte.
Lo script e questo (lb.rb):
#!/usr/bin/ruby # encoding: UTF-8 # lb.rb File.open("testo.txt").each_byte { | byte | print byte, " " } puts
Notare il codice 10
, corrispondente a \n
(o meglio ^L), l’a-capo.
Invece delle graffe è naturalmente possibile usare la coppia do
– end
, così (lb1.rb):
#!/usr/bin/ruby # encoding: UTF-8 # lb.rb File.open("testo.txt").each_byte do | byte | print byte, " " end puts
Ruby, al solito, consente di usare il modo che si preferisce.
Leggere un carattere per volta.
Dalla versione 1.9 (potete verificare quella installata con ruby -v
) è possibile leggere un byte per volta visualizzando i caratteri invece del codice ASCII (l1b.rb):
#!/usr/bin/ruby # encoding: UTF-8 # l1b.rb File.open("testo.txt").each_char { | byte | print byte, " " } puts
Cambiare nome al file
Semplicissimo, c’è rename
, ecco come rinominare vecchio.txt
in nuovo.txt
(ren.rb):
#!/usr/bin/ruby # encoding: UTF-8 # ren.rb File.rename "vecchio.txt", "nuovo.txt"
Attenzione:come al solito non sono necessarie le parentesi ma se si mettono c’è un bug (probabilmente):
quest’istruzione funziona:
File.rename("vecchio.txt", "nuovo.txt")
questa invece da errore:
File.rename ("vecchio.txt", "nuovo.txt")
Naturalmente con Ruby è possibile copiare e cancellare files, creare directories (cartelle) e operazioni simili; se serve ne faccio un post.
Trovare la posizione in un file
Con pos
è possibile determinare la posizione nel file; usiamo il file di testo testo.txt
di prima vediamo come (posiz.rb):
#!/usr/bin/ruby # encoding: UTF-8 # posiz.rb f = File.open "testo.txt" puts "Inizio: #{f.pos}" f.gets puts "Dopo aver letto la prima riga: #{f.pos}" f.gets puts "Dopo aver letto la seconda riga: #{f.pos}"
Cioè pos
ci dice quanti bytes sono stati letti, o meglio, a quanti byte dall’inizio del file, è il puntatore di lettura. È così possibile spostarsi all’interno del file leggerne solo una parte, esempio (parte.rb):
#!/usr/bin/ruby # encoding: UTF-8 # parte.rb f = File.open "modif.txt" f.pos = 21 r = f.gets puts "ho letto: #{r}"
Scrivere in un file
Nel post precedente avevamo visto come scrivere in un file riga per riga.
Ma Ruby ci consente di inserire blocchi multilinea tutto in una volta, eccone un esempio (wb.rb):
#!/usr/bin/ruby # encoding: UTF-8 # wb.rb File.open "out.txt", "w" do |f| testo_da_scrivere = <<TESTO_DA_SCRIVERE Questo è il testo che viene scritto nel file "out.txt". Come si vede sono più righe anche con righe vuote e tutti i caratteri che voglio, senza bisogno di delimitarlo con apici o virgolette. E ci sono anche i rientri. fine. TESTO_DA_SCRIVERE f.puts testo_da_scrivere end
Come si vede il blocco di testo è quello contenuto all’interno dell’identificatore, definito con <<
e chiuso ripetendolo. Ho usato TESTO_DA_SCRIVERE
ma può essere quel che si vuole; per convenzione si usa scriverlo tutto in maiuscolo.
Modalità di apertura
Nell’esempio precedente il file è stato aperto in scrittura, vedi il parametro "w"
alla riga 6. per la lettura si usa "r"
che essendo di default viene spesso omessa. Ma ce ne sono altre:
r
: lettura, il puntatore è all’inizio del file;r+
: sono possibili sia la lettura che la scrittura, il puntatore è all’inizio del file;w
: solo scrittura, se il file non esiste viene creato, se esiste viene sovrascritto;w+
: comer+
con la differenza che se non esiste viene creato;a
: append-mode. Il puntatore viene posizionato alla fine del file per cui è possibile scrivere testo aggiuntivo senza perdere il contenuto esistente;a+
: comea
con la possibilità di lettura;b
: binary mode, per leggere i file non di testo.
Appendere testo a un file
Usando out.txt
(creato prima) vediamo come possiamo inserire l’ora, con la modalità a appena descritta (ora.rb):
#!/usr/bin/ruby # encoding: UTF-8 # ora.rb f = File.open "out.txt", "a" f.puts "\nData odierna:#{Time.now.to_s}\n"
Memorizzare oggetti in un file
Finora abbiamo visto come mettere testo in un file, adesso vediamo come è possibile mettere oggetti o istanze a classi nei files.
Pstore
Pstore è un formato binario con il quale è possibile registrare quasi tutto. Vediamo come possiamo registrare una classe; per prima cosa creiamo il file della classe (quad_class.rb):
#!/usr/bin/ruby # encoding: UTF-8 # quad_class.rb class Quad #quadrato attr_accessor :lun_lato def initialize lun_lato = 0 @lun_lato = lun_lato end def area @lun_lato * @lun_lato end def perimetro @lun_lato * 4 end end
Adesso creiamo lo script pstore_write.rb:
#!/usr/bin/ruby # encoding: UTF-8 # pstore_write.rb require './quad_class.rb' s1 = Quad.new s1.lun_lato = 4 s2 = Quad.new s2.lun_lato = 7 require 'pstore' store = PStore.new('quadrati') store.transaction do store[:quad] ||= Array.new store[:quad] << s1 store[:quad] << s2 end
Esaminiamo il file: require './quad_class.rb'
alla riga 6 include il file quad_class.rb
dentro lo script, come se l’avessimo scritto qui; in questo modo lo script risulta più piccolo e il file della classe può essere riciclato più volte.
creiamo poi due quadrati s1
e s2
, righe 8-11.
Alla riga 13 includiamo il file pstore
(di sistema, cioè fornito con Ruby) in modo da poterne usare il formato.
Con la riga 14 creiamo il file quadrati e con le righe 15-19 ci scriviamo dentro i quadrati s1
e s2
creati prima nell’array quad
.
||=
significa che l’array non è necessario crearlo se già esistente.
A questo punto possiamo eseguire lo script:
Come si vede viene creato il file quadrati, 41 byte. Il file è binario e non riuscite a leggerlo con un file di testo; se siete curiosi potete usare un edito esadecimale, ecco cosa ottenete:
È giunto il momento di creare lo script per leggere i dati memorizzati in quadrati, così (pstore_read.rb):
#!/usr/bin/ruby # encoding: UTF-8 # pstore_read.rb require './quad_class.rb' require 'pstore' store = PStore.new('quadrati') quad_m = [] store.transaction do quad_m = store[:quad] end quad_m.each do |quad| puts "Lato = #{quad.lun_lato}" puts "Area = #{quad.area}" puts "Perimetro = #{quad.perimetro}" puts end
Come si vede i due quadrati vengono visualizzati. I lati vengono ritrovati dal file quadrati, con le istruzioni delle righe 9-13.
In particolare viene creato l’array quad_m
vuoto (riga 10) riempito con il ciclo delle righe 11-13.
Il ciclo delle righe 15-20 visualizza i quadrati. Notare che il calcolo di area e perimetro vengono svolti solo in questa fase e scritti direttamente senza memorizzarli in una variabile.
Il manuale che sto seguendo (Karthikeyan) propone anche un altro metodo di immagazzinamento dei dati; è solo una variante del precedente. Non lo riporto perché non aggiunge niente di nuovo.
E poi il post è già troppo lungo 😉