我需要保存一些关于一些文件的信息。没什么太花哨的,所以我想我会用一个简单的每个项目文本文件一行。像这样的东西:
# 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。我讨厌评估不是从可信数据生成的字符串的想法。
所以。意见?是不是有一些库可以做到这一切?我错过了一些明显的东西吗?你会怎么做?