Boost.IostreamsのFilterを使う!

Boost.IostreamsにはFilterというものがあります。
これを使うことでストリームから入力、ストリームへ出力するデータを加工できます。

#include <iostream>
#include <boost/iostreams/filtering_stream.hpp>
#include <boost/iostreams/filter/gzip.hpp>
#include <boost/iostreams/device/file_descriptor.hpp>

int main(){
    namespace io = boost::iostreams;

    //データをgzip圧縮してファイルに書き出し
    io::filtering_ostream out;
    out.push(io::gzip_compressor());
    out.push(io::file_descriptor_sink("gzip_file.gz"));
    out << "Example characters";
    io::close(out); //データをflush

    //ファイルからデータを読み込んでgzip展開
    io::filtering_istream in;
    in.push(io::gzip_decompressor());
    in.push(io::file_descriptor_source("gzip_file.gz"));

    std::string str;
    in >> str;

    std::cout << str << std::endl;

    return 0;
}

Output:

Example

"Example characters"と出力されそうな気がしますが、これは>>演算子を使ったため、std::cinと同じようにスペースを区切りとして出力されました。
ひとまず、これはおいておきます。


上記のコードでは、まずfiltering_ostreamに、DualUseFilter*1であるgzip_compressorをpushしました。
そしてファイルへ出力するSinkであるfile_descriptor_sinkをpushしました。
これで、データを出力するとき、データはgzip_compressorを通って圧縮され、file_descriptor_sinkに流れてファイルに出力されます。
filtering_ostreamでは、データはFilterを通ってSinkへと、pushした順番に流れていきます。

次に、filtering_istreamに、DualUseFilterのgzip_decompressorを、そしてSourceのfile_descriptor_sourceをpushしました。
file_descriptor_sourceでファイルからデータが読み込まれ、gzip_decompressorで展開されて、変数に入力されます。
filtering_istreamでは、pushしたFilterからSourceとは逆の順番で、データが流れていきます。


SinkもSourceも、必ず一番最後にpushします。これがデータの流れ着く先、湧き出る元となるので。

ここではFilterはひとつだけ使いました。Filterは0個以上使える、ということになっています。
つまりFilterを何も使わず、Deviceのみをpushし、io::filtering_streamをio::streamと同じく扱うこともできます。
(ただ、Deviceしか使わないなら、io::streamを使ったほうが余計な機能がなくていいかと思います)
そして、Filterを複数個使えば、文字列中の単語を置き換えて、データを圧縮して、暗号化して…といくつものデータ加工を行うことができます。

Filterの解説はここまでです。


さて、おいておかれてしまった、文字列"Example characters"の出力ですが、これは単純に、std::cinと同じようにstd::getlineで取得すればOKです。

int main(){
    namespace io = boost::iostreams;

    io::filtering_istream in;
    in.push(io::gzip_decompressor());
    in.push(io::file_descriptor_source("gzip_file.gz"));

    std::string str;
    std::getline(in, str);

    std::cout << str << std::endl;

    return 0;
}


せっかくなのでIostreamsにある関数、copyを使ってみましょう。

#include <boost/iostreams/copy.hpp>

int main(){
    namespace io = boost::iostreams;

    io::filtering_istream in;
    in.push(io::gzip_decompressor());
    in.push(io::file_descriptor_source("gzip_file.gz"));

    std::string str;
    io::copy(in, io::back_inserter(str));

    std::cout << str << std::endl;

    return 0;
}

copyは1行といわず全データをコピーします。


ところでこのFilterは定期的に掃除をしなくても大丈夫なのかしら。*2

*1:InputFilterとしてもOutputFilterとしても使えるFilter

*2:大丈夫です!!