【问题标题】:Someway to buffer or wrap cin so I can use tellg/seekg?以某种方式缓冲或包装 cin 以便我可以使用tellg/seekg?
【发布时间】:2017-11-26 11:04:16
【问题描述】:

有什么方法可以为 cin 添加缓冲,以便我可以在该 istream 上有效地使用 tellg 和 seekg? (我只需要返回大约 6 个字符。)或者是否有某种方法可以使用(可能是自定义的)istream 对象来包装流,该对象充当缓冲管道,允许我使用tellg/seekg 来恢复流位置几个字符?它可能看起来像这样:

BufferedIStream bis(cin);
streampos pos = bis.tellg();
MyObjectType t = getObjectType(bis);
bis.seekg(pos);

作为一种解决方法,我目前正在将 cin 读取到 EOF 到一个字符串中,并将该字符串传输到 istringstream,但这有许多我想避免的负面影响。

我能想到的唯一另一件事是使用私有版本(仅由工厂使用)重载我所有数据类上的所有扫描/读取函数,其中假定标头已被使用,因此我可以完全消除对tellg/seekg 的需要。这可以正常工作,但会引入相当多的丑陋。相比之下,tellg/seekg 与我的工厂隔离,只有两行代码。我讨厌扔掉它。

【问题讨论】:

  • 我推荐 Nicolai Josuttis 的 C++ 标准库。不过,在编写了关于 IOStreams 的章节的相关部分后,我可能会有偏见。我知道的唯一其他选择是 Angelika Langer 和 Klaus Kreft 的 IOStreams and Locales Library。 Steve Teale 的 IOStreams 已经过时了(我也确实在那里学习了基础知识)。

标签: c++ cin istream


【解决方案1】:

您可以创建过滤流缓冲区,即从std::streambuf 派生的类。为了支持缓冲读取,一旦输入字符被消耗,您将覆盖 underflow() 以填充下一个字符缓冲区。为了支持有限的搜索,前一个缓冲区不会被丢弃,而是部分保留。此外,您将覆盖 seekoff()

这样的事情应该可以解决问题:

#include <iostream>
#include <streambuf>
#include <string>
#include <cstdlib>
#include <cstring>

class bufferbuf
    : public std::streambuf {
    enum { size = 2000, half = size / 2 };
    char            buffer[size];
    std::streambuf* sbuf;
    std::streamoff  base;
public:
    bufferbuf(std::streambuf* sbuf): sbuf(sbuf), base() {
        auto read = sbuf->sgetn(this->buffer, size);
        this->setg(this->buffer, this->buffer, this->buffer + read);
    }
    int underflow() {
        if (this->gptr() == this->buffer + size) {
            std::memmove(this->eback(), this->eback() + half, half);
            base += half;
            auto read = sbuf->sgetn(this->eback() + half, half);
            this->setg(this->eback(), this->eback() + half, this->eback() + half + read);
        }
        return this->gptr() != this->egptr()
            ? traits_type::to_int_type(*this->gptr())
            : traits_type::eof();
    }
    std::streampos seekoff(off_type                offset,
                           std::ios_base::seekdir  whence,
                           std::ios_base::openmode which) override {
        if (this->gptr() - this->eback() < -offset
            || this->egptr() - this->gptr() < offset
            || whence != std::ios_base::cur
            || !(which & std::ios_base::in)) {
            return pos_type(off_type(-1));
        }
        this->gbump(offset);
        return pos_type(this->base + (this->gptr() - this->eback()));
    }
    std::streampos seekpos(pos_type pos, std::ios_base::openmode which) override {
        if (off_type(pos) < this->base
            || this->base + (this->egptr() - this->eback()) < off_type(pos)
            || !(which & std::ios_base::in)) {
           return pos_type(off_type(-1));
        }
        this->setg(this->eback(), this->eback() + (off_type(pos) - this->base), this->egptr());
        return pos_type(base + (this->gptr() - this->eback()));
    }
};

int main() {
    bufferbuf buf(std::cin.rdbuf());
    std::istream in(&buf);
    // ...
    std::string s0, s1;
    bool relative(false);
    if (relative) {
        while (in >> s0
               && (in.seekg(-int(s0.size()), std::ios_base::cur), in >> s1)) {
            std::cout << "read "
                      << "s0='" << s0 << "' " << "s1='" << s1 << "'\n";
        }
    }
    else {
        for (std::streampos pos = in.tellg();
             in >> s0 && (in.seekg(pos), in >> s1); pos = in.tellg()) {
            std::cout << "read "
                      << "s0='" << s0 << "' " << "s1='" << s1 << "'\n";
        }
    }
}

上面的代码适用于几个简单的测试用例。它演示了相对定位和绝对定位的使用。一般来说,我发现在流中搜索是没有用的,因为通常每个有趣的词法分析都可以通过一个字符前瞻来完成。结果,我可能在职位方面错过了一些东西。不过,我希望上面的代码能够正常工作。

【讨论】:

  • @MatthewBusche:对于初学者来说,你可能想说明什么实际上不起作用以及你尝试过什么[而不是抱怨你被否决了]。除了 std::streambuf* sbuf; 成员的小错字之外,代码实际上确实按预期工作(考虑到它是在还不错的 iPhone 上键入的)。不过,它显然不支持绝对定位,也就是说,您需要实现绝对定位(通过覆盖 seekpos() 并可能保持全局位置)或者您需要使用相对定位进行回溯。
  • @MatthewBusche:您测试了我的代码的哪个版本?我今天更新了代码,我认为它应该适用于您的代码。
  • 缓冲区现在很好用。我已经删除了所有引用旧版本的帖子,因为它们与未来的读者无关。非常感谢您的帮助。我去看看书!
猜你喜欢
  • 2016-05-28
  • 1970-01-01
  • 2015-07-05
  • 2020-02-22
  • 1970-01-01
  • 1970-01-01
  • 2011-10-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多