2016-07-29 18 views
4

私の質問はthisと似ていますが、この問題のC++リファレンスは見つかりませんでした。複数のファイルをC++で連続してストリームする

読み込みおよび処理する大きなファイルのリストがあります。前のファイルの最後に次のファイルを自動的に開く、ファイルからデータを1つずつ取得する入力ストリームを作成する最善の方法は何ですか?このストリームは、ファイル境界を越えて可変サイズのブロックを順次読み取る処理関数に渡されます。

+0

まあ、「Unixy:

class multifilebuf : public std::filebuf { public: multifilebuf(std::initializer_list<std::string> filenames) : next_filenames(filenames.begin() + 1, filenames.end()) { open(*filenames.begin(), std::ios::in); } protected: std::streambuf::int_type underflow() override { for (;;) { auto res = std::filebuf::underflow(); if (res == traits_type::eof()) { // done with this file, move onto the next one if (next_filenames.empty()) { // super done return res; } else { // onto the next file close(); open(next_filenames.front(), std::ios::in); next_filenames.pop_front(); continue; } } else { return res; } } } private: std::deque<std::string> next_filenames; }; 

その方法は、あなたがエンドユーザーに透過的にすべてを行うことができますが"あなたのプログラムをフィルタとして書く(stdinから読み込んでstdoutに書き込む)、そして' cat input_file * .dat | myprogram'と呼ばれる。しかし、詳細な説明がなくても(つまり、グロブリングが可能な名前を持つディレクトリ内のすべてのファイルや、さまざまな場所に広がっている、または順序が異なる必要がある)、それ以上のことは言い難いです... – twalberg

+0

'std :: istream'から派生した新しいクラスを作成することができます。このクラスには、std :: vectorの' std :: vector'が含まれています。これは、EOF上で自動的に次のものに切り替わるか、または読み込みに失敗します。 – KABoissonneault

+0

バッファファイルに集めて読み込み彼らの後に? 2部操作 – Charlie

答えて

4

あなたがしたいことは、std::basic_streambufから継承するタイプを提供することです。多くの暗黙のvirtualメンバ関数があり、そのうちの関連するものはshowmanyc(),underflow()uflow()、およびxsgetn()です。オーバフロー時に、リスト内の次のファイル(もしあれば)を自動的に開くように、それらをオーバーロードする必要があります。

ここに実装例があります。私たちは、std::filebufとして機能し、ちょうど私たちが読む必要がある次のファイルのdeque<string>保つ:

multifilebuf mfb{"file1", "file2", "file3"}; 

std::istream is(&mfb); 
std::string word; 
while (is >> word) { 
    // transaparently read words from all the files 
} 
+0

これらのことは次の質問で紹介される予定です。私はC++に関するすべてのことを知っていると主張する人に尋ねるでしょう。ニースを見つける! – KABoissonneault

+0

@KABoissonneaultさらに進んで、実用的な例を作る方法を考え出しました。私はこのケースがそれほど悪くないと思っていて、 'underflow()'だけが必要でした。 – Barry

0

簡単な解決策として、boostの結合を使用してファイルのistreamイテレータの範囲を指定します。私は現在のC++ライブラリで同様の機能を認識していませんが、おそらくTS Rangesv3に存在します。

あなた自身でも書くことができます:自分で書くことは完全に可能です。

私はこれを「平坦化」入力専用イテレータとして記述します。イテレータは、各範囲の内容を順に繰り返し処理する範囲の範囲にわたってイテレータです。イテレータは、将来の範囲の範囲を追跡し、現在の要素のイテレータを保持します。

Hereは、書かなければならないコードの大きさのアイデアを与える非常に単純なジッパーイテレータです(ジップイテレータは別のコンセプトです。これはfor(:)ループにのみ適した単純なものです)。

これはC++ 14を使用してそれを行うかもしれない方法のスケッチです:

template<class It> 
struct range_t { 
    It b{}; 
    It e{}; 
    It begin() const { return b; } 
    It end() const { return e; } 
    bool empty() const { return begin()==end(); } 
}; 

template<class It> 
struct range_of_range_t { 
    std::deque<range_t<It>> ranges; 
    It cur; 
    friend bool operator==(range_of_range_t const& lhs, range_of_range_t const& rhs) { 
    return lhs.cur==rhs.cur; 
    } 
    friend bool operator!=(range_of_range_t const& lhs, range_of_range_t const& rhs) { 
    return !(lhs==rhs); 
    } 
    void operator++(){ 
    ++cur; 
    if (ranges.front().end() == cur) { 
     next_range(); 
    } 
    } 
    void next_range() { 
    while(ranges.size() > 1) { 
     ranges.pop_front(); 
     if (ranges.front().empty()) continue; 
     cur = ranges.front().begin(); 
     break; 
    } 
    } 
    decltype(auto) operator*() const { 
    return *cur; 
    } 
    range_of_range_t(std::deque<range_t<It>> in): 
    ranges(std::move(in)), 
    cur{} 
    { 
    // easy way to find the starting cur: 
    ranges.push_front({}); 
    next_range(); 
    } 
}; 

それはイテレータの公理のすべてをサポートしなければならないという点で、イテレータは、作業が必要。そして、イテレータを正しく終了させることは少しの作業です。

これは、ストレーマではなく、イテレータです。

関連する問題