2011年9月14日水曜日

String#unpack!

VC++とWindowsで遊ぼうかと思ったら、VC2010のSP1インストールで盛大にトラブってまだ終わらないので、Rubyの小ネタで場をつなぐ。

Rubyでフォーマットのあるバイナリを読み込む場合、String#unpackを使うのがよくあるパターン。とはいえ使いづらい点もあって

  1. フォーマット文字列が長さ情報を持っているのに、何バイト読んだかの情報が取れない
  2. unpack自体はデータを消費しないので、次のデータを読もうとすると自分で進めないといけない

これで、例えばデータ自体の内容によってフォーマットが切り替わるようなデータを処理しようとすると、フォーマットから長さを計算して固定長切り出してフォーマットに従ってunpackして……という鉄板コードが並ぶことに。

やってられないので、こんなのを考えてみた。

class String
def unpack!(fmt)
ret = unpack(fmt)
slice!(0,ret.pack(fmt).size)
ret
end
end
# Use like this.
str = "\0\xaa\x01\x55\xff"
until str.empty?
case str.unpack!('C').first
when 0
puts str.unpack('C')
when 1
puts str.unpack('n')
end
end
view raw str_unpack.rb hosted with ❤ by GitHub


unpackした結果をpackし直して長さを求めるという手抜きかつ富豪な実装だけど、これだけでデータ処理コード自体の見通しはだいぶ良くなったりする。

0 件のコメント:

コメントを投稿