5

假设我有一个ifstream代表一个包含许多聚合在一起的子文件的大文件。我希望能够istream从代表文件一部分的较大ifstream(给定大小和offest)创建一个“子”,以便其他代码可以从该子流中读取,就好像它是一个独立的istream.

关于我如何做到这一点的任何想法?

编辑 - 我宁愿避免提升。

4

4 回答 4

6

这是一个流缓冲区“过滤器”的示例,它从包含的流缓冲区中读取,从指定位置开始并读取到指定大小。您创建substreambuf、传递您的原始文件streambufsubstreambuf然后转换访问权限,以便从底层中的所需位置读取所有内容streambuf

sgetc调用和调用所涉及snextc的大部分开销应该优化掉。许多提取操作符是逐字节工作的,因此除了维护小节内的读取位置和检查小节的结尾之外,不应该有额外的开销。当然,使用此类读取大块数据的效率会降低(尽管可以修复)。underflowuflow

这仍然需要改进,例如测试请求的位置是否在底层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
}

这受到过滤 Streambufs的启发

于 2011-10-05T06:15:41.703 回答
1

我使用Boost.Iostreams库做了类似的事情。查看教程|书写设备。这个想法是创建一个“设备”类来实现低级接口(读/写/搜索),然后使用您的设备类实例化一个 istream/ostream 派生类来执行实际的 I/O。

于 2011-10-01T22:25:05.603 回答
1

所有 iostream 都将大部分自定义逻辑放在其streambuf专业化中。fstream(或)使用 的实例进行basic_fstream初始化。( )相同。如果你想滚动你自己的子流,你可以通过实现你自己的父流来实现。istreamfile_bufstringstreamstringbufstreambuf

于 2011-10-04T04:23:23.490 回答
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
}
于 2011-10-04T05:42:57.170 回答