Je dois enregistrer quelques informations sur certains fichiers. Rien de trop chic, alors j'ai pensé que j'irais avec une simple ligne par fichier texte. Quelque chose comme ceci:Evasion en toute sécurité et relecture d'un chemin de fichier dans ruby
# 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 parce qu'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 tas 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. Nous devons maintenant nous échapper.
note: J'aime l'espace comme un délimiteur d'enregistrement, mais je n'aurais aucun problème à le changer pour un plus facile à utiliser. Dans le cas des noms de fichiers cependant, le seul qui pourrait aider est ascii nul "\ 0" mais un fichier délimité nul n'est plus vraiment un fichier texte ...
J'ai d'abord eu un mur de texte détaillant les itérations de ma lutte pour faire une bonne fonction d'échappement et sa réciprocité mais c'é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. Bien trop long et akward, je pense que ça ne devrait pas être si dur.
Tout en essayant de faire ce travail que j'ai essayé de trouver d'autres façons:
la bittorrent manière codée/php serialize: le préfixe du nom de fichier avec la longueur du nom puis juste io.read (name_len.to_i). Cela fonctionne mais c'est un vrai pita pour éditer le fichier à la main. À ce stade, nous sommes à mi-chemin d'un format binaire. String # inspect: Celui-ci a l'air expressément fait à cet effet! Sauf qu'il semble que la seule façon de récupérer la valeur est l'évaluation. Je déteste l'idée d'évaluer une chaîne que je n'ai pas générée à partir de données fiables.
Donc. Des avis ? N'y at-il pas de lib qui peut faire tout cela? Est-ce que je manque quelque chose d'évident? Comment feriez-vous cela ?
ah csv! N'y avais pas pensé. Probablement parce que je ne suis pas fan du format et de ses règles baroques (et souvent redéfinies). Mais c'est là, et ça fonctionne. Et puisque je choisis l'auteur et le lecteur, je n'aurai aucun problème de compatibilité, n'est-ce pas? Sérieusement, je teste le module et il semble assez solide. Je devrais réécrire toute la logique autour du lecteur mais c'est normal quand on fait ce genre de changement de framework. – user336851
module réussi à gérer tout ce que je l'ai jeté. – user336851