Question

Je dois sauver quelques informations sur certains fichiers. Rien d'extraordinaire, donc je pensais que je serais aller avec une simple ligne par fichier texte de l'élément. Quelque chose comme ceci:

# 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

Bien sûr, cela ne fonctionne pas car un nom de fichier peut contenir des espaces (rupture scanf) et les sauts de ligne (rupture IO # chacun).

Le problème de saut de ligne peut être évité en laissant tomber l'utilisation de chacun et aller avec un groupe de gets ( » « )

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

Jongler avec les espaces dans les noms est une autre affaire. Maintenant, nous devons faire un peu échapper.
Note: Je aime l'espace comme un délimiteur d'enregistrement mais je n'ai pas question de changer pour une plus facile à utiliser. Dans le cas des noms de fichiers bien, le seul qui pourrait aider est ascii NUL « \ 0 », mais un fichier délimité NUL n'est pas vraiment un fichier texte plus ...

J'ai eu d'abord un mur de texte détaillant les itérations de ma lutte pour faire une fonction correcte et échapper à sa réciproque, mais il était juste ennuyeux et pas vraiment utile. Je vais vous donner le résultat final:

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

Je ne suis pas content du tout avec read_name. Trop long et akward, je pense qu'il ne devrait pas être difficile.

Tout en essayant de faire ce travail que j'ai essayé de trouver d'autres façons:

  • la manière encodée / php serialize bittorrent: préfixe le nom du fichier avec la longueur du nom puis juste io.read (name_len.to_i). Il fonctionne, mais il est un vrai pita pour éditer le fichier manuellement. À ce stade, nous sommes à mi-chemin à un format binaire.

  • Chaîne # inspectez: Celui-ci semble expressément à cette fin! Sauf qu'il semble que la seule façon d'obtenir le retour de la valeur est par eval. Je déteste l'idée de eval-ing une chaîne que je ne générait pas de données fiables.

. Des avis ? N'y at-il une lib qui peut faire tout cela? Est-ce que je manque quelque chose évidente? Comment voulez-vous faire?

Était-ce utile?

La solution

Quand vous dites « sauver » Entreposez-vous: les informations dans un fichier?

Vous pouvez utiliser le Module CSV du Ruby Bibliothèque standard. Cela signifie que votre delimiter est la virgule plutôt que de l'espace, mais il gérerait tous les échapper et déséchapper pour vous.

  • Si une valeur contient des espaces que la valeur est enfermée dans "quotes"

  • Si une valeur contient des guillemets alors un caractère de citation est échappée comme 2 guillemets par exemple "hello" deviendrait """hello"""

Pour écrire les détails dans un fichier:

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

Pour les relire:

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

Autres conseils

CSV, comme mentionné, est un bon choix. Une autre est YAML ( "YAML Is not Markup Language"), qui peut traiter des données plus arbitraires que peut CSV. Voici quelques données:

require 'pp'
require 'yaml'

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

L'écriture de laisser les données dans un fichier au format YAML:

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

Maintenant, nous allons voir ce que l'YAML ressemble à:

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

Et enfin nous allons reconstituer les données du fichier YAML:

pp YAML.load_file('/tmp/foo.yaml')
# => {:exclamation=>"Yabba Dabba Doo",
# =>  :first_name=>"Fred",
# =>  :last_name=>"Flinstone",
# =>  :children=>["Bam Bam", "Pebbles"]}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top