且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

如何在C ++中编写自定义输入流

更新时间:2022-12-11 17:52:21

C ++中的新流是从 std :: streambuf 派生并覆盖 underflow()操作读取和 overflow() sync()写操作。为了你的目的,你可以创建一个过滤流缓冲区,它接收另一个流缓冲区(可能还有一个流,可以使用 rdbuf()作为参数提取流缓冲区)

The proper way to create a new stream in C++ is to derive from std::streambuf and to override the underflow() operation for reading and the overflow() and sync() operations for writing. For your purpose you'd create a filtering stream buffer which takes another stream buffer (and possibly a stream from which the stream buffer can be extracted using rdbuf()) as argument and implements its own operations in terms of this stream buffer.

流缓冲区的基本轮廓将是这样:

The basic outline of a stream buffer would be something like this:

class compressbuf
    : public std::streambuf {
    std::streambuf* sbuf_;
    char*           buffer_;
    // context for the compression
public:
    compressbuf(std::streambuf* sbuf)
        : sbuf_(sbuf), buffer_(new char[1024]) {
        // initialize compression context
    }
    ~compressbuf() { delete[] this->buffer_; }
    int underflow() {
        if (this->gptr() == this->egptr()) {
            // decompress data into buffer_, obtaining its own input from
            // this->sbuf_; if necessary resize buffer
            // the next statement assumes "size" characters were produced (if
            // no more characters are available, size == 0.
            this->setg(this->buffer_, this->buffer_, this->buffer_ + size);
        }
        return this->gptr() == this->egptr()
             ? std::char_traits<char>::eof()
             : std::char_traits<char>::to_int_type(*this->gptr());
    }
};

如何 underflow()看起来完全取决于我使用的压缩库大多数库我使用保持一个内部缓冲区需要填充,并保留尚未消耗的字节通常,很容易钩子解压缩到 underflow )

How underflow() looks exactly depends on the compression library being used. Most libraries I have used keep an internal buffer which needs to be filled and which retains the bytes which are not yet consumed. Typically, it is fairly easy to hook the decompression into underflow().

创建流缓冲区后,只需初始化 std :: istream object with the stream buffer:

Once the stream buffer is created, you can just initialize an std::istream object with the stream buffer:

std::ifstream fin("some.file");
compressbuf   sbuf(fin.rdbuf());
std::istream  in(&sbuf);

如果要经常使用流缓冲区,可能需要将对象构造封装到类,例如 icompressstream 。这样做有点棘手,因为基类 std :: ios 是一个虚拟基础,是流缓冲区存储的实际位置。要在传递指向 std :: ios 的指针之前构造流缓冲区,因此需要跳过几个圈:它需要使用 / code>基类。下面是大致的情况:

If you are going to use the stream buffer frequently, you might want to encapsulate the object construction into a class, e.g., icompressstream. Doing so is a bit tricky because the base class std::ios is a virtual base and is the actual location where the stream buffer is stored. To construct the stream buffer before passing a pointer to a std::ios thus requires jumping through a few hoops: It requires the use of a virtual base class. Here is how this could look roughly:

struct compressstream_base {
    compressbuf sbuf_;
    compressstream_base(std::streambuf* sbuf): sbuf_(sbuf) {}
};
class icompressstream
    : virtual compressstream_base
    , public std::istream {
public:
    icompressstream(std::streambuf* sbuf)
        : compressstream_base(sbuf)
        , std::ios(&this->sbuf_)
        , std::istream(&this->sbuf_) {
    }
};

(我只是输入这个代码没有一个简单的方法来测试它是相当正确;但总体方法应该按照描述工作)

(I just typed this code without a simple way to test that it is reasonably correct; please expect typos but the overall approach should work as described)