一部のファイルに関するいくつかの情報を保存する必要があります。あまり凝ったものではないので、1 項目につき 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 を使用することです。信頼できるデータから生成したのではない文字列を評価するという考えは嫌いです。
そう。意見 ? これをすべて実行できるライブラリはありませんか?明らかな何かが欠けていますか?どうやってそれをしますか?