Ruby – files II

r10

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

lb

Notare il codice 10, corrispondente a \n (o meglio ^L), l’a-capo.
Invece delle graffe è naturalmente possibile usare la coppia doend, 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

l1b

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"

ren

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}"

posiz

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}"

parte

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

wb

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+ : come r+ 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+ : come a 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"

ora

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:

pstore_write

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:

click

click

È 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

pstore_read

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 😉

Posta un commento o usa questo indirizzo per il trackback.

Commenti

  • dikiyvolk  Il 2 settembre 2013 alle 10:26

    Ciao Juhan,

    Ma non è vero che il post è troppo lungo quelli interessanti si leggono sempre con piacere 🙂

Rispondi

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

Logo di WordPress.com

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

Google photo

Stai commentando usando il tuo account Google. 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 )

Connessione a %s...

Questo sito utilizza Akismet per ridurre lo spam. Scopri come vengono elaborati i dati derivati dai commenti.

%d blogger hanno fatto clic su Mi Piace per questo: