ifstream
一緒に集約された多数のサブファイルを含む大きなファイルを表すがあるとします。他のコードがそのサブストリームから独立istream
した.ifstream
istream
これを達成する方法についてのアイデアはありますか?
編集 - ブーストは避けたいと思います。
これは、指定された位置から開始して指定されたサイズまで読み取る、含まれている streambuf から読み取る streambuf "フィルター" の例です。を作成し、元の をsubstreambuf
渡してアクセスを変換し、基になる の目的の場所からすべてが読み取られるようにします。streambuf
substreambuf
streambuf
sgetc
とsnextc
fromの呼び出しに伴うオーバーヘッドのほとんどは、最適化して取り除く必要がunderflow
あります。uflow
多くの抽出演算子はバイトごとに機能するため、サブセクション内の読み取り位置を維持し、サブセクションの終わりを確認する以外に追加のオーバーヘッドが発生することはありません。もちろん、このクラスでは大量のデータを読み取ると効率が低下します (ただし、これは修正される可能性があります)。
これには、要求された場所が基になる 内にあることをテストするなどの改善が必要streambuf
です。
class substreambuf : public std::streambuf
{
public:
substreambuf(std::streambuf *sbuf, std::size_t start, std::size_t len) : m_sbuf(sbuf), m_start(start), m_len(len), m_pos(0)
{
std::streampos p = m_sbuf->pubseekpos(start);
assert(p != std::streampos(-1));
setbuf(NULL, 0);
}
protected:
int underflow()
{
if (m_pos + std::streamsize(1) >= m_len)
return traits_type::eof();
return m_sbuf->sgetc();
}
int uflow()
{
if (m_pos + std::streamsize(1) > m_len)
return traits_type::eof();
m_pos += std::streamsize(1);
return m_sbuf->sbumpc();
}
std::streampos seekoff(std::streamoff off, std::ios_base::seekdir way, std::ios_base::openmode which = std::ios_base::in | std::ios_base::out)
{
std::streampos cursor;
if (way == std::ios_base::beg)
cursor = off;
else if (way == std::ios_base::cur)
cursor = m_pos + off;
else if (way == std::ios_base::end)
cursor = m_len - off;
if (cursor < 0 || cursor >= m_len)
return std::streampos(-1);
m_pos = cursor;
if (m_sbuf->pubseekpos(m_start + m_pos, std::ios_base::beg) == std::streampos(-1))
return std::streampos(-1);
return m_pos;
}
std::streampos seekpos(std::streampos sp, std::ios_base::openmode which = std::ios_base::in | std::ios_base::out)
{
if (sp < 0 || sp >= m_len)
return std::streampos(-1);
m_pos = sp;
if (m_sbuf->pubseekpos(m_start + m_pos, std::ios_base::beg) == std::streampos(-1))
return std::streampos(-1);
return m_pos;
}
private:
std::streambuf *m_sbuf;
std::streampos m_start;
std::streamsize m_len;
std::streampos m_pos;
};
こんな感じで使えます
using namespace std;
void somefunc(ifstream &bigifs)
{
substreambuf sbuf(bigifs.rdbuf(),100,100);
//new istream with the substreambuf as its streambuf
istream isub(&sbuf);
//use isub normally
}
すべての iostream は、カスタム ロジックのほとんどをstreambuf
特殊化に配置します。fstream
(またはbasic_fstream
)istream
のインスタンスで初期化しますfile_buf
。stringstream
( )も同様stringbuf
。独自のサブストリーム ストリームを展開したい場合streambuf
は、親ストリームに関して独自に実装することで実行できます。
Boost.Iostreamsライブラリを使用して、このようなことを行いました。チュートリアル|ライティングデバイスの下を見てください。アイデアは、低レベル インターフェイス (読み取り/書き込み/シーク) を実装する "デバイス" クラスを作成し、デバイス クラスを使用して istream/ostream 派生クラスをインスタンス化して、実際の I/O を実行することです。
ちょっとしたアイデア: コードのクライアント側 (つまり、入力ストリームを使用する部分) を制御できる場合は、以下に示すように、2 つの追加パラメーターを受け入れるようにコードを変更することをお勧めします。
// Old code
void ClassUsingInput::SetInput(std::streambuf & inputbuf)
{
// Implementation ...
}
になることができる :
// New code
void ClassUsingInput::SetInput(std::streambuf & inputbuf, std::streampos position, std::streamsize size)
{
inputbuf.pubseekpos(position) ;
// internally use size to detect end-of-substream
}