2017-06-23 76 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

緩衝區現在工作的很好。我刪除了涉及舊版本的所有帖子,因爲它們與未來的讀者無關。非常感謝您的幫助。我會看看這本書! –

相關問題