1

我需要保存一些关于一些文件的信息。没什么太花哨的,所以我想我会用一个简单的每个项目文本文件一行。像这样的东西:

# 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

当然这不起作用,因为文件名可以包含空格(破坏 scanf)和换行符(破坏 IO#each)。

可以通过放弃使用 each 并使用一堆 get(' ') 来避免换行问题

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

处理名称中的空格是另一回事。现在我们需要做一些转义。
注意:我喜欢空格作为记录分隔符,但我可以毫无问题地更改它以使其更易于使用。但是,在文件名的情况下,唯一可以提供帮助的是 ascii nul "\0" 但 nul 分隔文件不再是真正的文本文件......

我最初有一堵文字墙,详细说明了我为制作正确的转义函数及其互惠而奋斗的迭代过程,但这很无聊,而且没有真正的用处。我只会给你最终的结果:

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

我对 read_name 一点也不满意。太长太难了,我觉得不应该那么难。

在尝试完成这项工作时,我尝试提出其他方法:

  • bittorrent 编码/php 序列化方式:在文件名前加上名称的长度,然后只需 io.read(name_len.to_i)。它可以工作,但手动编辑文件是一个真正的皮塔饼。在这一点上,我们已经完成了二进制格式的一半。

  • String#inspect :这个看起来是专门为此目的而制作的!除了似乎恢复价值的唯一方法是通过 eval。我讨厌评估不是从可信数据生成的字符串的想法。

所以。意见?是不是有一些库可以做到这一切?我错过了一些明显的东西吗?你会怎么做?

4

2 回答 2

1

当您说“保存”时,您的意思是将信息存储在文件中吗?

您可以使用 Ruby 标准库中的CSV 模块。这意味着您的分隔符是逗号而不是空格,但它会为您处理所有转义和取消转义。

  • 如果一个值包含空格,则该值包含在"quotes"

  • 如果一个值包含引号,则引号字符将转义为 2 个引号字符,例如"hello"将变为"""hello"""

要将详细信息写入文件:

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

要读回它们:

CSV::Reader.parse(File.open('csvout', 'rb')) do |row|
  p row
end
于 2010-05-13T20:52:16.680 回答
1

如前所述,CSV 是一个不错的选择。另一个是 YAML(“Yaml Ain't a Markup Language”),它可以处理比 CSV 更多的任意数据。这里有一些数据:

require 'pp'
require 'yaml'

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

让我们将数据写入 YAML 格式的文件:

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

现在让我们看看 YAML 的样子:

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

最后让我们从 YAML 文件中重构数据:

pp YAML.load_file('/tmp/foo.yaml')
# => {:exclamation=>"Yabba Dabba Doo",
# =>  :first_name=>"Fred",
# =>  :last_name=>"Flinstone",
# =>  :children=>["Bam Bam", "Pebbles"]}
于 2010-05-13T22:01:01.983 回答