【问题标题】:substream from istream来自 istream 的子流
【发布时间】:2011-10-01 22:09:38
【问题描述】:

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

关于我如何实现这一点的任何想法?

编辑 - 我宁愿避免提升。

【问题讨论】:

    标签: c++ file istream


    【解决方案1】:

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

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

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

    这是受到Filtering Streambufs的启发

    【讨论】:

    • underflow 方法不应该更新 m_read,是吗?
    • 提供的代码示例充满了问题,不应被视为正确答案。 underflow 函数不应递增 readuflow 返回值应该是 sbuf-&gt;sbumpc();,而不是 m_sbuf-&gt;snextc();。提供的代码也不将seek 操作计入read 值(一开始它的有用性值得怀疑)。
    • 我解决了这些问题。如果我错过了其他任何内容,请随时编辑。 @cmbasnett,除了实施错误之外,您是说这只是按照海报要求的错误方式吗?
    • 我尝试使用此代码时的小注意事项,实现非常幼稚,并且性能低于标准。在此版本中,每读取一个字节都会调用 uflow 命令。这种形式在学术界之外毫无价值。
    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

      所有 iostream 都将大部分自定义逻辑放在其 streambuf 特化中。 fstream(或basic_fstream)使用file_buf 的实例初始化istreamstringstream (stringbuf) 也一样。如果您想滚动自己的子流,可以通过在父流中实现自己的streambuf 来实现。

      【讨论】:

        【解决方案4】:

        一点想法:如果您可以控制代码的客户端(即使用输入流的部分),我建议您修改它以接受两个附加参数,如下图所示:

        // 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
        }
        

        【讨论】:

          猜你喜欢
          • 2010-12-29
          • 2011-10-30
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-12-10
          • 1970-01-01
          • 2019-06-03
          • 1970-01-01
          相关资源
          最近更新 更多