【问题标题】:C++: buffer the cin istreamC++:缓冲 cin istream
【发布时间】:2018-04-24 10:50:03
【问题描述】:

问题是:

我有一个在功能齐全的istream 上运行的代码。它使用如下方法:

istream is;
is.seekg(...) // <--- going backwards at times
is.tellg()    // <--- to save the position before looking forward
etc.

这些方法仅适用于来自文件的 istream。但是,如果我以这种方式使用cin,它将不起作用--cin 没有保存位置、向前读取然后返回保存位置的选项。

// So, I can't cat the file into the program
cat file | ./program

// I can only read the file from inside the program
./program -f input.txt

// Which is the problem with a very, very large zipped file
// ... that cannot coexist on the same raid-10 drive system 
// ... with the resulting output
zcat really_big_file.zip | ./program //<--- Doesn't work due to cin problem
./program -f really_big_file.zip //<--- not possible without unzipping

我可以将cin 读入deque,并处理deque。 1mb deque 缓冲区绰绰有余。然而,这在三个意义上是有问题的:

  1. 我必须用deque 重写所有内容才能做到这一点
  2. 它不会像使用 istream 那样防弹,因为它的代码已经被调试过
  3. 似乎,如果我将它实现为 deque 有一些困难,有人会过来说,你为什么不像 ___ 那样做呢

创建可用的istream 对象的正确/最有效方法是什么,因为所有成员都处于活动状态,cin istream

(记住性能很重要)

【问题讨论】:

  • 虽然您无法在std::cin 上进行随机访问,但您始终可以在std::cin 上“寻找”前进,只需从中读取(丢弃您阅读的内容)。假设您想向前“寻找” 1000 个字符,然后将 read 1000 个字符放入您不使用的缓冲区中。对于较大的文件,循环读取块。
  • 你可以看看 boost.iostream;更具体地说,basic_array device 提供了一种将一些连续的字符数组视为可搜索 iostream 的方法...
  • ... 可以与 back_insert_device 结合使用,例如,boost 循环缓冲区
  • 嗯,该帖子中提议的 streambuf 子类实现对我来说看起来不完整......但我可能是错的,这就是为什么我会使用现成的解决方案,因为所有 iostream 要求都正确,这很无聊而且因此容易出错/耗时......请注意,boost iostream 的那部分只是标题,所以使用它应该没有问题
  • 此外,该帖子中第二个答案的作者建议与 boost.iostream 一起提升进程间流。这些为您的问题 IMO 提供了最佳解决方案,无论是通过缓冲流(安全地映射没有副本的 c 数组)或矢量流(允许交换连续容器,一个非常好的优雅解决方案)。这些看起来也只有标题。

标签: c++ input file-io io


【解决方案1】:

cin 是用户输入,应被视为不可预测。如果您想使用上述功能并且您确定您的输入,您可以将整个输入读取到istringstream,然后对其进行操作

【讨论】:

  • 这似乎不适用于大于 ram 的文件,但字符串流不需要将所有内容都读入内存吗?
  • 它需要。所以你正在尝试实现一种实时输入处理程序?
  • 不,这是一个典型的用例。请记住,如果您想读取 stringstream 的整个输入,则必须从 cin 读取它
  • 为我的误解道歉。如果你有一个文件,为什么不在你的程序中读取它并使用支持seekg 等的fstream 而不是将该文件放入cin
【解决方案2】:

当获取新数据但缓冲所有接收到的字符时,您可以创建一个从std::cin 读取的过滤流缓冲区。您将能够在输入的缓冲范围内实现搜索。超出已缓冲输入的末尾将意味着读取相应数量的数据。下面是一个对应实现的例子:

#include <iostream>
#include <vector>

class bufferbuf
    : public std::streambuf {
private:
    std::streambuf*   d_sbuf;
    std::vector<char> d_buffer;

    int_type underflow() {
        char buffer[1024];
        std::streamsize size = this->d_sbuf->sgetn(buffer, sizeof(buffer));
        if (size == 0) {
            return std::char_traits<char>::eof();
        }
        this->d_buffer.insert(this->d_buffer.end(), buffer, buffer + size);
        this->setg(this->d_buffer.data(),
                   this->d_buffer.data() + this->d_buffer.size() - size,
                   this->d_buffer.data() + this->d_buffer.size());
        return std::char_traits<char>::to_int_type(*this->gptr());
    }
    pos_type seekoff(off_type off, std::ios_base::seekdir whence, std::ios_base::openmode) {
        switch (whence) {
        case std::ios_base::beg:
            this->setg(this->eback(), this->eback() + off, this->egptr());
            break;
        case std::ios_base::cur:
            this->setg(this->eback(), this->gptr() + off, this->egptr());
            break;
        case std::ios_base::end:
            this->setg(this->eback(), this->egptr() + off, this->egptr());
            break;
        default: return pos_type(off_type(-1)); break;
        }
        return pos_type(off_type(this->gptr() - this->eback()));
    }
    pos_type seekpos(pos_type pos, std::ios_base::openmode) {
        this->setg(this->eback(), this->eback() + pos, this->egptr());
        return pos_type(off_type(this->gptr() - this->eback()));
    }
public:
    bufferbuf(std::streambuf* sbuf)
        : d_sbuf(sbuf)
        , d_buffer() {
        this->setg(0, 0, 0); // actually the default setting
    }
};

int main() {
    bufferbuf      sbuf(std::cin.rdbuf());
    std::istream   in(&sbuf);
    std::streampos pos(in.tellg());

    std::string line;
    while (std::getline(in, line)) {
        std::cout << "pass1: '" << line << "'\n";
    }
    in.clear();
    in.seekg(pos);
    while (std::getline(in, line)) {
        std::cout << "pass2: '" << line << "'\n";
    }
}

此实现在将输入传递到读取步骤之前对其进行缓冲。您可以读取单个字符(例如将char buffer[1024]; 更改为char buffer[1]; 或使用sbumpc() 适当地替换sgetn() 的使用)以提供更直接的响应:在即时响应和批处理性能之间存在权衡处理。

【讨论】:

  • 这看起来很不错,是适应我的情况的一个很好的起点。你的代码非常好,当然它比我预期的要多得多——谢谢!
猜你喜欢
  • 2020-10-24
  • 1970-01-01
  • 1970-01-01
  • 2020-07-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多