Мне нужно сохранить немного информации о некоторых файлах. Ничего особенного, поэтому я подумал, что мне нужно будет добавить одну строчку для каждого текстового файла. Примерно так:
# 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 # каждый).
Проблему разрыва строки можно избежать, отказавшись от использования каждого из них и выполнив кучу 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. Ненавижу идею об оценке строки, которую я не сгенерировал из надежных данных.
Итак. Мнения? Есть ли какая-нибудь библиотека, которая может сделать все это? Я что-то упускаю из виду? Как бы вы это сделали?