バイナリのみのデータをstd::ifstreamで読み込む時にはまったのでメモ。 # 意外とというか忘れている。。。
バイナリデータの読み込み(失敗)
例えば32bitのデータが連続している下記のようなデータがある。
test.dat
12345678 00112233 44556677 8899aabb ccddeeff
-------- -----------------------------------
00000000:00000000 00000001 00000002 00000003
00000010:00000004 00000005 00000006 00000007
00000020:00000008 00000009 0000000a 0000000b
00000030:0000000c 0000000d 0000000e 0000000f
数値で0-fまで32bitずつ。この書き方だとbig endianになる。
以下のようなコードで読み込めると思っていたんだけど。 思ったように行かない。
std::ifstream ifs("./test.dat", std::ios::binary); std::vector<std::int32_t> v; v.assign(std::istream_iterator<std::int32_t>(ifs), std::istream_iterator<std::int32_t>()); std::for_each(v.begin(), v.end(), [](std::int_t i) {std::cout << i << " ";});
ちなみに環境はgcc 4.8.2だけど多分どれもダメ。 oprator >>(std::int_t)が32bitずつ読み込むようなことをしてくれない。
回避策
そういう場合は、32bit幅でreadするようにoperator >>をオーバーロードする。 ただ、std::int32_tの挙動は変えられないので、以下のように変換用のクラスを作る。 このクラスはstd::int32_t型と暗黙に変換できるようにしておく。
class int32_reader { public: int32_reader(std::int32_t i = -1) : i_(i) {} operator std::int32_t(void) const {return i_;} private: friend std::istream& operator >>(std::istream& is, int32_reader& w); std::int32_t i_; }; std::istream& operator >>(std::istream& is, int32_reader& w) { is.read(reinterpret_cast<char*>(&w.i_), sizeof(w.i_)); return is; } int main(int argc, char* argv[]) { std::ifstream ifs("./test.dat", std::ios::binary); std::vector<std::int32_t> v; v.assign(std::istream_iterator<int32_reader>(ifs), std::istream_iterator<int32_reader>()); std::for_each(v.begin(), v.end(), [](std::int32_t i) {std::cout << i << " ";}); return 0; }
これで32bitずつデータを読み込めるようになる。
なんかもっと良い方法がありそう。。
おまけ
一応、入出力対応でテンプレート化してみた。