Безопасно избегать и чтение пука в рубине
Вопрос
Мне нужно сэкономить несколько информации о некоторых файлах. Ничего не слишком любить, поэтому я подумал, что я пойду с простой одной строкой на предмет текстового файла. Что-то вроде этого :
# 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. Слишком долго и Akward, я чувствую, что не должен быть так сложно.
При попытке сделать эту работу, я пытался придумать другие способы:
BitTorrent Conded / PHP Serialize Way: префикс имени файла с длиной имени, а затем только io.read (name_len.to_i). Это работает, но это настоящая пита для редактирования файла вручную. На данный момент мы на полпути к двоичному формату.
String # Осмотрите: этот выглядит прямо для этой цели! За исключением того, что это похоже на единственный способ получить значение обратно через Eval. Я ненавижу идею Eval-ing a String, которую я не генерировал от доверенных данных.
Так. Мнения? Разве есть ли некоторые lib, которые могут сделать все это? Я упускаю что-то очевидное? Как бы Вы это сделали ?
Решение
Когда вы говорите «Сохранить», вы имеете в виду магазин информации в файле?
Вы могли бы использовать CSV модуль из библиотеки Ruby Standard. Это будет означать, что ваш разделитель является запятой, а не пространством, но это будет справиться с всеми выходами и неуклюжими для вас.
Если значение содержит пробелы, что значение прилагается в
"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
Другие советы
CSV, как уже упоминалось, это хороший выбор. Другой - ямл («Ямл не кладет язык разметки»), который может обрабатывать более произвольные данные, чем 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"]}