Domanda

Ho bisogno di salvare un paio di informazioni su alcuni file. Niente di stratosferico così ho pensato di andare con un semplice una linea per ogni file di testo oggetto. Qualcosa di simile a questo:

# write
io.print "%i %s %s\n" % [File.mtime(fname), fname, Digest::SHA1.file(fname).hexdigest]
# read
io.each do |line|
  mtime, name, hash = line.scanf "%i %s %s"
end

Naturalmente questo non funziona perché un nome di file può contenere spazi (rottura scanf) e interruzioni di riga (rottura IO # ciascuna).

Il problema interruzione di linea può essere evitato facendo cadere l'uso di ogni e andare con un gruppo di gets (' ')

while not io.eof?
  mtime = Time.at(io.gets(" ").to_i)
  name = io.gets " "
  hash = io.gets "\n"
end

Trattare con gli spazi nei nomi è un'altra questione. Ora abbiamo bisogno di fare un po 'la fuga.
nota: mi piace lo spazio come delimitatore di record, ma mi piacerebbe avere nessun problema cambiarla per una più facile da usare. Nel caso di nomi di file, però, l'unico che potrebbe aiutare è ASCII nul "\ 0", ma un file nul delimitato in realtà non è un file di testo più ...

Inizialmente ho avuto un muro di testo dettaglio le iterazioni della mia lotta per fare una corretta funzione di fuga e il suo reciproco, ma era solo noioso e non molto utile. Mi limiterò a darvi il risultato finale:

def write_name(io, val)
  io << val.gsub(/([\\ ])/, "\\\\\\1") # yes that' 6 backslashes !
end

def read_name(io)
  name, continued = "", true
  while continued
    continued = false
    name += io.gets(' ').gsub(/\\(.)/) do |c|
      if c=="\\\\"
        "\\"
      elsif c=="\\ "
        continued=true
        " "
      else
        raise "unexpected backslash escape  : %p (%s %i)" % [c, io.path, io.pos]
      end
    end
  end
  return name.chomp(' ')
end

Non sono felice a tutti con read_name. Troppo lungo e fastidioso, sento che non dovrebbe essere così difficile.

Durante il tentativo di fare questo lavoro ho cercato di trovare altri modi:

  • bittorrent codificato / php serialize modo: il prefisso del nome del file con la lunghezza del nome poi basta io.read (name_len.to_i). Funziona ma è un vero e proprio pita per modificare il file a mano. A questo punto siamo a metà strada per un formato binario.

  • String # ispezionare: questo sembra espressamente a tale scopo! Solo che sembra che l'unico modo per ottenere la schiena valore è attraverso eval. Odio l'idea di valuta-zione di una stringa non ho generare dai dati attendibili.

. Opinioni? Non c'è qualche lib che può fare tutto questo? Mi sto perdendo qualcosa di ovvio? Come hai fatto?

È stato utile?

Soluzione

Quando si dice "Salva" ti archivio media le informazioni in un file?

Si potrebbe utilizzare il CSV modulo dal Rubino standard Library. Ciò significa che il vostro delimitazione è una virgola invece di spazio ma sarebbe gestire tutta la fuga e unescaping per voi.

  • Se un valore contiene spazi che il valore è racchiuso in "quotes"

  • Se un valore contiene citazioni quindi un carattere di citazione è sfuggito come 2 caratteri di citazione per esempio "hello" sarebbe diventato """hello"""

Per scrivere i dettagli in un file:

require 'csv'

outfile = File.open('csvout', 'wb')
CSV::Writer.generate(outfile) do |csv|
  csv << [File.mtime(fname), fname, Digest::SHA1.file(fname).hexdigest]
end
outfile.close

Per leggere di nuovo:

CSV::Reader.parse(File.open('csvout', 'rb')) do |row|
  p row
end

Altri suggerimenti

CSV, come detto, è una buona scelta. Un altro è YAML ( "YAML non è un linguaggio di markup"), in grado di gestire i dati di più di quanto possa arbitrari CSV. Ecco alcuni dati:

require 'pp'
require 'yaml'

h = {
  :first_name => 'Fred',
  :last_name => 'Flinstone',
  :children => ['Bam Bam', 'Pebbles'],
  :exclamation => 'Yabba Dabba Doo',
}

Let scrivere i dati in un file in formato YAML:

File.open('/tmp/foo.yaml', 'w') do |file|
  file.write h.to_yaml
end

Ora vediamo quello che sembra il YAML come:

$ cat /tmp/foo.yaml
---
:exclamation: Yabba Dabba Doo
:first_name: Fred
:last_name: Flinstone
:children:
- Bam Bam
- Pebbles

E infine cerchiamo di ricostituire i dati dal file YAML:

pp YAML.load_file('/tmp/foo.yaml')
# => {:exclamation=>"Yabba Dabba Doo",
# =>  :first_name=>"Fred",
# =>  :last_name=>"Flinstone",
# =>  :children=>["Bam Bam", "Pebbles"]}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top