Pergunta

Preciso salvar algumas informações sobre alguns arquivos. Nada muito chique, então eu pensei em ir com uma linha de texto simples por item. Algo assim :

# 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

É claro que isso não funciona porque um nome de arquivo pode conter espaços (quebra de scanf) e quebras de linha (quebrando IO#cada).

O problema da quebra da linha pode ser evitado soltando o uso de cada um e indo com um monte de gets ('')

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

Lidar com espaços nos nomes é outra questão. Agora precisamos escapar.
Nota: eu gosto do espaço como delimitador de registro, mas não teria problema em alterá -lo para um mais fácil de usar. No caso de nomes de arquivos, porém, o único que poderia ajudar é o ASCII NUL " 0", mas um arquivo delimitado nu não é realmente realmente um arquivo de texto ...

Inicialmente, tive uma parede de texto detalhando as iterações da minha luta para fazer uma função de fuga correta e seu recíproco, mas era apenas chato e não é realmente útil. Vou apenas te dar o 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

Não estou feliz com read_name. Muito tempo e Akward, sinto que não deveria ser tão difícil.

Enquanto tentava fazer esse trabalho, tentei inventar outras maneiras:

  • O BitTorrent codificado / php serializa maneira: prefixo o nome do arquivo com o comprimento do nome e depois apenas io.read (name_len.to_i). Funciona, mas é um verdadeiro pita editar o arquivo manualmente. Neste ponto, estamos no meio do caminho para um formato binário.

  • String#Inspecione: Este parece feito expressamente feito para esse fim! Exceto que parece que a única maneira de recuperar o valor é através da avaliação. Eu odeio a ideia de avaliar uma string que não gera a partir de dados confiáveis.

Então. Opiniões? Não existe alguma lib que possa fazer tudo isso? Estou perdendo algo óbvio? Como você faria isso ?

Foi útil?

Solução

Quando você diz "salvar", você quer dizer armazenar as informações em um arquivo?

Você poderia usar o Módulo CSV Da biblioteca padrão do Ruby. Isso significaria que seu delimitador é vírgula e não espaço, mas lidaria com toda a fuga e descontagem para você.

  • Se um valor contiver espaços, esse valor está entre "quotes"

  • Se um valor contiver cotações, um caractere de cotação é escapado como 2 caracteres de cotação, por exemplo, "hello" se tornaria """hello"""

Para escrever os detalhes em um arquivo:

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 lê -los de volta:

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

Outras dicas

O CSV, como mencionado, é uma boa escolha. Outra é Yaml ("Yaml não é uma linguagem de marcação"), que pode lidar com dados mais arbitrários do que o CSV. Aqui estão alguns dados:

require 'pp'
require 'yaml'

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

Vamos escrever os dados em um arquivo no formato YAML:

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

Agora vamos ver como é o YAML:

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

E, finalmente, vamos reconstituir os dados do arquivo YAML:

pp YAML.load_file('/tmp/foo.yaml')
# => {:exclamation=>"Yabba Dabba Doo",
# =>  :first_name=>"Fred",
# =>  :last_name=>"Flinstone",
# =>  :children=>["Bam Bam", "Pebbles"]}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top