substream from istream

六眼飞鱼酱① 提交于 2019-12-21 18:33:44

问题


Suppose I have an ifstream which represents a large file containing lots of sub-files aggregated together. I want to be able to create a "sub" istream from the larger ifstream (given a size and offest) representing a part of the file so other code can read from that substream as if it was an independent istream.

Any ideas on how I might accomplish this?

EDIT - I would prefer to avoid boost.


回答1:


This is an example of a streambuf "filter" that reads from a contained streambuf starting at a specified location and reading up to a specified size. You create substreambuf, passing your original streambuf in and substreambuf then translates access so that everything is read from the desired location in the underlying streambuf.

Most of the overhead involved in calling sgetc and snextc from underflow and uflow should optimize away. Many extraction operators work byte by byte, so there should not be additional overhead beyond maintaining the read position within the subsection and checking for the end of the subsection. Of course, reading large chunks of data will be less efficient with this class (although that could be fixed).

This still needs improvements like testing that the requested location is within the underlying 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;
};

It can be used like this

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
}

This was inspired by Filtering Streambufs




回答2:


I've done something like this using the Boost.Iostreams library. Look under Tutorial|Writing Devices. The idea is to create a "device" class which implements the low-level interface (read/write/seek) and then instantiate an istream/ostream derived class using your device class to do the actual I/O.




回答3:


All iostreams put most of their custom logic in their streambuf specializations. fstream (or basic_fstream) initializes istream with an instance of file_buf. Same for stringstream (stringbuf). If you want to roll your own substream stream, you can do it by implementing your own streambuf in terms of a parent stream.




回答4:


Just a little idea : If you have control over the client side of the code (i.e. the part that uses the input stream), I suggest you modify it to accept two additional parameters, like illustrated below :

// Old code
void ClassUsingInput::SetInput(std::streambuf & inputbuf)
{
   // Implementation ...
}

Can become :

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


来源:https://stackoverflow.com/questions/7623277/substream-from-istream

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!