2017-06-23 75 views
0

有没有办法给cin添加缓冲区,这样我就可以有效地在这个istream上使用tellg和seekg了? (我只需要返回大约6个字符。)或者,也许有一种方法可以用一个(可能是自定义的)istream对象封装流,这个对象充当缓冲管道,允许我使用tellg/seekg来恢复流位置几个字符?这可能是这样的:为了缓冲或包裹cin,我可以使用tellg/seekg吗?

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

作为一种变通,我目前正在读CIN到EOF成一个字符串,而该字符串转移至istringstream,但有许多消极的一面,会影响我d喜欢避免。

我唯一能想到的其他事情就是在所有数据类上使用私有版本(仅由工厂使用)重载所有数据类的所有扫描/读取功能,假设头已被占用,所以我可以完全消除对tellg/seekg的需求。这样可以很好地工作,但会引入相当数量的丑陋。相比之下,tellg/seekg与我的工厂是隔离的,只是两行代码。我讨厌倾倒它。

+1

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

回答

2

您可以创建一个过滤流缓冲区,即从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"; 
     } 
    } 
} 

上面的代码与几个简单的测试情况下工作。它演示了相对和绝对定位的用法。一般来说,我发现在流中寻找是没有用的,因为通常每个有趣的选择都可以通过一个字符来完成。结果,我可能在位置上错过了一些东西。不过,我期望上面的代码正常工作。

+0

@MatthewBusche:对于初学者,您可能想要说明哪些方面实际上不起作用,以及您尝试过什么(而不是抱怨说您得到了低估)。除了'std :: streambuf * sbuf;'成员的小错字外,代码实际上按预期工作(考虑到它是在iPhone上输入的并不是那么糟糕)。它显然不支持绝对定位,也就是说,您需要实现绝对定位(通过重写'seekpos()'并且可能保持全局位置),或者您需要使用相对定位进行回溯。 –

+0

@MatthewBusche:我测试过哪个版本的代码?我今天更新了代码,我认为它应该适用于您的代码。 –

+0

缓冲区现在工作的很好。我删除了涉及旧版本的所有帖子,因为它们与未来的读者无关。非常感谢您的帮助。我会看看这本书! –

相关问题