假设我有一个ifstream
代表一个包含大量聚合在一起的子文件的大文件。我希望能够从较大的istream
(给定大小和offest)创建一个“sub”ifstream
来代表文件的一部分,以便其他代码可以从该子流中读取,就像它是一个独立的istream
。
关于如何实现这一目标的任何想法?
修改 - 我宁愿避免提升。
答案 0 :(得分:5)
这是一个streambuf“过滤器”的示例,它从包含的streambuf读取,从指定位置开始并读取到指定的大小。您创建substreambuf
,将原始streambuf
传入和substreambuf
然后翻译访问权限,以便从基础streambuf
中的所需位置读取所有内容。
从sgetc
和snextc
调用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
}
的启发
答案 1 :(得分:1)
我使用Boost.Iostreams库完成了类似的操作。在教程|编写设备下查看。我们的想法是创建一个“设备”类,它实现低级接口(读/写/搜索),然后使用您的设备类实例化一个istream / ostream派生类来执行实际的I / O.
答案 2 :(得分:1)
所有iostream都将大多数自定义逻辑放在streambuf
专精中。 fstream
(或basic_fstream
)使用istream
的实例初始化file_buf
。 stringstream
(stringbuf
)也是如此。如果您想要滚动自己的子流,可以通过在父流方面实现自己的streambuf
来实现。
答案 3 :(得分:0)
一点点想法:如果您可以控制代码的客户端(即使用输入流的部分),我建议您修改它以接受另外两个参数,如下图所示:
// 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
}