攔截 std::cout 的輸出結果

| | 0 Comments| 15:36
Categories:

一般在寫 C++ 的程式的時候,大多會用標準輸出(standard output)來輸出文字、或是偵錯用的資訊。在標準函式庫裡面,針對輸出的部分就有提供 std::coutstd::cerrstd::clog,以及各自對應的寬字元版本。

但是在開發圖形介面的時候,往往不會顯示 console 視窗,所以這些訊息也就都看不到了。

而如果想要去攔截這些輸出的資訊,並將它們顯示在圖形介面裡面,該怎麼做呢?實際上,C++ 是允許是透過 rdbuf() 這個函式(參考),來取得或設定一個 stream 內部要使用的 stream buffer(參考)的。

也因此,透過這樣的機制,實際上是可以讓 std::cout 把資料寫到另外準備好的 stream buffer 中,然後再讀出來的!

下面就是一個簡單的實作:

#include <iostream>
#include <sstream>
 
int main(int argc, char** argv)
{
  // get standard buffer
  auto* pStdBuf = std::cout.rdbuf();
 
  // use another stringbuf
  std::stringbuf buf;
  std::cout.rdbuf(&buf);
 
  // output
  std::cout << "Hello world\n";
  std::cout << 1 << 2.0 << "\n";
 
  /// get content
  std::string sText = buf.str();
 
  // restore buffer
  std::cout.rdbuf(pStdBuf);
 
  // output
  std::cout << "Captured content:\n{\n" << sText << "}\n";
}

這邊就是先透過 pStdBuf 來記錄本來 std::cout 內部使用的 stream buffer。

然後,這邊則是建立一個 std::stringbuf 的物件 buf、並透過 rdbuf() 來要求 std::cout 改用它來做為內部的記憶體儲存。

所以接下來透過 std::cout 輸出的內容,就都不會直接輸出到 console 視窗了、而是會記錄在 buf 裡了~

而如果要取得 buf 的內容,也可以直接透過 str() 這個函式來取得,算是滿方便的。

之後,這邊則是再透過 rdbuf() 恢復 std::cout 本來的內部 stream buffer,最後再把資料剛剛得到的字串輸出,作為驗證。

而如果想要把結果直接輸出到檔案的話,這邊也可以透過 std::fstream 來取代 std::stringbuf

而如果 std::cerr 也需要的,那也可以做同樣的操作。


基本上這樣是可以實作到一定的程度了,但是有幾點可能要注意:

  1. C 的 printf() 的東西不會被攔截到,要另外想辦法處理。
    但是這邊好像沒有找到可以跨平台使用的方案。
  2. std::cout 是全域物件(global object),所以這邊修改設定就會整個程式受影響;相對地,如果別的地方也有在做類似的操作,就可能會出現預期外的狀況。
  3. 在透過呼叫 std::stringbufstr() 函式取得內部的資料的時候,不會清除內部的資料;所以如果是要持續去取得新的資料的話,可能會需要在取得資料後清除已經讀出來的資料。這邊可以透過 buf.str("") 來做到類似的效果,但是在多執行序的環境下可能要另外做處理。

參考:Capture stdout in a Qt Application

Leave a Reply

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *