Frage

Ich brauche ein paar Informationen über einige Dateien zu speichern. Nichts Besonderes, so dachte ich, ich mit einer einfachen pro Artikel einer Zeile Textdatei gehen würde. So etwas wie folgt aus:

# 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

Natürlich funktioniert das nicht, weil ein Dateiname Leerzeichen (Bruch scanf) und Zeilenumbrüche (Bruch IO # jeden) enthalten kann.

Der Zeilenumbruch Problem kann durch Fallenlassen der Verwendung jeden und geht mit einem Haufen bekommt (‘‚)

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

Der Umgang mit Leerzeichen in den Namen ist eine andere Sache. Jetzt müssen wir einige entkommen tun.
Anmerkung: Ich mag den Raum als Satzbegrenzer aber ich würde kein Problem haben es für eine einfacher zu bedienen ändern. Im Falle von Dateinamen aber der einzige, der helfen könnte ascii nul „\ 0“ ist aber eine nul getrennte Datei ist nicht wirklich eine Textdatei mehr ...

Ich hatte zunächst eine Wand aus Text die Iterationen meines Kampfes Detaillierung eine richtige Flucht Funktion und ihre gegenseitigen zu machen, aber es war einfach langweilig und nicht wirklich nützlich. Ich werde Ihnen nur das Endergebnis:

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

Ich bin überhaupt mit read_name nicht glücklich. Viel zu lange und akward, halte ich es nicht so schwer sein sollte.

Bei dem Versuch, diese Arbeit zu machen habe ich versucht, mit anderen Möglichkeiten zu kommen:

  • die Bittorrent-codiert / php serialize Art und Weise: Präfix des Dateiname mit der Länge des Namen dann nur io.read (name_len.to_i). Es funktioniert, aber es ist eine echte Fladen die Datei von Hand zu bearbeiten. An diesem Punkt sind wir auf halbem Weg in ein binäres Format.

  • String # inspizieren: diese sieht ausdrücklich zu diesem Zweck gemacht! Außer es wie der einzige Weg scheint, ist der Wert wieder zu bekommen durch eval. Ich hasse die Vorstellung von eval-ing eine Zeichenfolge ich nicht aus vertrauenswürdigen Daten haben erzeugen.

So. Meinungen? Gibt es nicht einige lib, die all dies tun können? Bin ich etwas fehlt offensichtlich? Wie würden Sie das tun?

War es hilfreich?

Lösung

Wenn Sie sagen, „save“ Wollen Sie damit sagen speichert die Informationen in einer Datei?

könnten Sie verwenden die CSV Modul von Ruby Standard Library. Dies würde bedeuten, dass Ihr Trennzeichen Komma anstatt Raum, aber es wäre für Sie alle entkommen und URL-Kodierung zu behandeln.

  • Wenn ein Wert Leerzeichen enthält diesen Wert in eingeschlossen ist "quotes"

  • Wenn ein Wert Anführungszeichen enthält dann ein Anführungszeichen als 2 Anführungszeichen beispielsweise entkam "hello" würde """hello"""

Um die Details in eine Datei zu schreiben:

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

, sie wieder zu lesen:

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

Andere Tipps

CSV, wie bereits erwähnt, ist eine gute Wahl. Ein weiterer Grund ist YAML ( „YAML kein Markup Language“), die mehr beliebige Daten als CSV kann verarbeiten kann. Hier einige Daten:

require 'pp'
require 'yaml'

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

Lassen Sie uns schreiben, um die Daten in eine Datei in YAML Format:

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

Nun wollen wir sehen, was die YAML wie folgt aussieht:

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

Und schließlich lassen Sie uns die Daten aus der YAML-Datei wiederherzustellen:

pp YAML.load_file('/tmp/foo.yaml')
# => {:exclamation=>"Yabba Dabba Doo",
# =>  :first_name=>"Fred",
# =>  :last_name=>"Flinstone",
# =>  :children=>["Bam Bam", "Pebbles"]}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top