Segura de escapar y volver a leer una ruta de archivo en Ruby
Pregunta
necesito para ahorrar unos cuantos informaciones sobre algunos archivos. Nada lujoso, así que pensé que iría con un simple una línea por cada archivo de texto del elemento. Algo como esto:
# 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
Por supuesto, esto no funciona debido a un nombre de archivo puede contener espacios (scanf romper) y saltos de línea (IO # rompiendo cada uno).
El problema salto de línea se puede evitar dejar caer el uso de cada uno y va con un manojo de gets (' ')
while not io.eof?
mtime = Time.at(io.gets(" ").to_i)
name = io.gets " "
hash = io.gets "\n"
end
Tratamiento de espacios en los nombres es otra cosa. Ahora tenemos que hacer un poco de escape.
nota: me gusta el espacio como un delimitador de registro, pero no tendría ningún problema cambiándolo por uno más fácil de usar. En el caso de los nombres de archivo, sin embargo, el único que podría ayudar es nulo ASCII "\ 0", sino un fichero nulo delimitado no es realmente un archivo de texto ya ...
Yo inicialmente tenía un muro de texto que detalla las iteraciones de mi lucha para hacer una función de escape correcta y su recíproco pero fue simplemente aburrido y no es realmente útil. Sólo te voy a dar el resultado 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
No estoy contento en absoluto con read_name. Demasiado tiempo y sus inconveniencias, siento que no debería ser tan difícil.
Al tratar de hacer este trabajo he tratado de llegar a otras formas:
-
el bittorrent codificada / php serialize manera: prefijo del nombre de archivo con la longitud del nombre de a continuación, sólo io.read (name_len.to_i). Funciona pero es un verdadero pita para editar el archivo manualmente. En este momento estamos a mitad de camino a un formato binario.
-
Cadena # inspeccionar: Éste parece hecha expresamente para este fin! Salvo que parece que la única manera de conseguir la espalda valor es a través de eval. No me gusta la idea de eval-ción de una cadena que no generaron a partir de datos de confianza.
Así. Opiniones? ¿No hay alguna lib que puede hacer todo esto? Me estoy perdiendo algo obvio? ¿Cómo hacer eso?
Solución
Cuando se dice "Guardar" S guarda media la información en un archivo?
Se podría utilizar el CSV módulo del Rubí Biblioteca estándar. Esto significaría que el delimitador es una coma en lugar de espacio, sino que se ocuparía de todo el escape y la representación no literal para usted.
-
Si un valor contiene espacios que el valor está encerrado en
"quotes"
-
Si un valor contiene comillas a continuación, un carácter de comillas se escapó como 2 caracteres de comillas, por ejemplo,
"hello"
se convertiría en"""hello"""
Para escribir los datos en un archivo:
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
Para leer de nuevo:
CSV::Reader.parse(File.open('csvout', 'rb')) do |row|
p row
end
Otros consejos
CSV, como se ha mencionado, es una opción buena. Otra es YAML ( "Yaml no es un lenguaje de marcado"), que puede manejar datos más arbitraria que CSV lata. He aquí algunos datos:
require 'pp'
require 'yaml'
h = {
:first_name => 'Fred',
:last_name => 'Flinstone',
:children => ['Bam Bam', 'Pebbles'],
:exclamation => 'Yabba Dabba Doo',
}
Vamos a escribir los datos en un archivo en formato YAML:
File.open('/tmp/foo.yaml', 'w') do |file|
file.write h.to_yaml
end
Ahora vamos a ver lo que ve el YAML como:
$ cat /tmp/foo.yaml
---
:exclamation: Yabba Dabba Doo
:first_name: Fred
:last_name: Flinstone
:children:
- Bam Bam
- Pebbles
Y finalmente vamos a reconstituir los datos del archivo YAML:
pp YAML.load_file('/tmp/foo.yaml')
# => {:exclamation=>"Yabba Dabba Doo",
# => :first_name=>"Fred",
# => :last_name=>"Flinstone",
# => :children=>["Bam Bam", "Pebbles"]}