安全地转义并回读ruby中的文件路径

时间:2010-05-13 20:22:10

标签: ruby escaping filenames flat-file backslash

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

# 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#)。

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

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 encoded / php serialize方式:使用名称的长度为文件名添加前缀,然后只为io.read(name_len.to_i)。它可以工作,但手动编辑文件真的很棒。在这一点上,我们已经到了二进制格式的一半。

  • String#inspect:这个看起来就是为了这个目的!除了似乎通过eval获得价值的唯一方法。我讨厌评估我没有从可信数据生成的字符串。

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

2 个答案:

答案 0 :(得分:1)

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

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

  • 如果值包含值,则"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

答案 1 :(得分:1)

如上所述,CSV是一个不错的选择。另一个是YAML(“Yaml不是标记语言”),它可以处理比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"]}