一般在寫 C++ 的程式的時候,大多會用標準輸出(standard output)來輸出文字、或是偵錯用的資訊。在標準函式庫裡面,針對輸出的部分就有提供 std::cout
、std::cerr
、std::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
也需要的,那也可以做同樣的操作。
基本上這樣是可以實作到一定的程度了,但是有幾點可能要注意:
- C 的
printf()
的東西不會被攔截到,要另外想辦法處理。
但是這邊好像沒有找到可以跨平台使用的方案。 std::cout
是全域物件(global object),所以這邊修改設定就會整個程式受影響;相對地,如果別的地方也有在做類似的操作,就可能會出現預期外的狀況。- 在透過呼叫
std::stringbuf
的str()
函式取得內部的資料的時候,不會清除內部的資料;所以如果是要持續去取得新的資料的話,可能會需要在取得資料後清除已經讀出來的資料。這邊可以透過buf.str("")
來做到類似的效果,但是在多執行序的環境下可能要另外做處理。