1. 程式人生 > >C++標準流重定向及cout和cerr的區別

C++標準流重定向及cout和cerr的區別

前言

當我們把一個win32控制檯的程式重構成介面程式的時候,之前的所有cout輸出語句就變得沒用了,而不得不重新查詢替換成其他輸出顯示方式。或者要輸出大量資訊的時候,想要看某一步的輸出,卻很快地被新的輸出覆蓋了(尤其在輸出視窗的緩衝區設得比較小的時候)。在這些情況下,如果能快捷地將輸入輸出流重定向到檔案中,一切就顯得迎刃而解了。

正文

一、C++標準輸入輸出流的重定向

C++的標準輸出流cout預設是輸出到顯示裝置中,標準輸入流cin預設是從鍵盤中讀取資料。而在很多情況下,列印到螢幕的資訊太多而來不及檢視,我們想把輸出的東西儲存到檔案中,以便後續的檢視分析。利用cout的重定向就可以在輸出到螢幕和輸出到檔案之間輕鬆切換。

1、利用cmd

#include <iostream>
#include <fstream>
using namespace std;
 
void main()
{
    cout<<"Hello world"<<endl; 
    char line[100];
    cin>>line;
    cout<<line<<endl;
}
上述程式碼是一個簡單的演示例子,輸出一行“Hello world”,讀入一個字串並且打印出來,在vs2010中執行時,結果如下:(其中第二行是從鍵盤輸入的字元)

要把cout和cin重定向到檔案中,方法如下:

開啟cmd,轉到程式目錄下,執行命令:

TestStream < input.txt > output.txt

即可,其中TestStream是.exe的檔名。


Input.txt存放的是要輸入的字串,需要實現新建好。output.txt是輸出檔案,會自動生成。執行後就把字元輸出到檔案中,而不列印到螢幕。

 

2、利用rdbuf函式

上面是利用cmd把cout和cin重定向到檔案中的方法,但是在利用visual studio開發的時候,總是利用cmd執行程式就顯得很不方便,此時的解決方法如下:

#include <iostream>
#include <fstream>
using namespace std;
 
ifstream fin("input.txt");
ofstream fout("output.txt");
streambuf *cinbackup;
streambuf *coutbackup;
void main()
{
    coutbackup= cout.rdbuf(fout.rdbuf());
    cinbackup= cin.rdbuf(fin.rdbuf());
    cout<<"Hello world"<<endl; 
    char line[100];
    cin>>line;
    cout<<line<<endl;
    // restore standard streambuf
    cin.rdbuf(cinbackup);
    cout.rdbuf(coutbackup);
}

其中rdbuf是流緩衝區設定的函式,它有兩種過載形式:

get (1)

streambuf* rdbuf() const;

set (2)

streambuf* rdbuf (streambuf* sb);

Get/set stream buffer

The first form (1) returnsa pointer to the stream buffer object currently associated with the stream.
The second form (2) also sets the object pointed by sb as the stream buffer associated with the stream and clears the .

cout.rdbuf(fout.rdbuf())這個語句的意思就是把cout的流緩衝區設定成ofstream的流緩衝區,這樣cout所輸出的資訊都被輸出到檔案中。

程式碼中最後兩句話是為了把標準輸入輸出流的緩衝區恢復成原有設定。

對於C形式的printf和scanf,上述設定並不起作用,可以用一下設定完成:

freopen("input.txt","r",stdin);
freopen("output.txt","w",stdout);

二、cout和cerr的區別

1、cout對應於標準輸出流

     cerr對應於標準錯誤流

2、cout和cerr的主要區別就是cout可以利用cmd命令列引數的方式進行重定向,而cerr則不行。

#include <iostream>
using namespace std;
 
void main()
{
    cout<<"Hello world"<<endl; 
    cerr<<"Hello error"<<endl;
    char line[100];
    cin>>line;
    cout<<line<<endl;
}

用cmd執行,螢幕打印出cerr的資訊,而cout的資訊被輸出到檔案中。



3、cerr不被緩衝,也就說錯誤訊息可以直接傳送到顯示器,而無需等到緩衝區或者新的換行符時,才被顯示。而cout是一個有緩衝的輸出。

關於這一點,很多的資料上都有提到,但是cerr也可以通過rdbuf方法重定向到檔案中

#include <iostream>
#include <fstream>
using namespace std;
 
ifstream fin("input.txt");
ofstream fout("output.txt");
streambuf *cinbackup;
streambuf *coutbackup;
void main()
{
    coutbackup= cout.rdbuf(fout.rdbuf());
    cinbackup= cin.rdbuf(fin.rdbuf());
    cerr.rdbuf(fout.rdbuf());
    cout<<"Hello world"<<endl; 
    cerr<<"Hello error"<<endl;
    char line[100];
    cin>>line;
    cout<<line<<endl;
    // restore standard streambuf
    cin.rdbuf(cinbackup);
    cout.rdbuf(coutbackup);
}

此時螢幕輸出如下:


output.txt檔案內容如下:


按照rdbuf的說明,這是設定流對應的緩衝區指標的,如果按照cerr不被緩衝的說法,那這個設定為什麼生效了?

查看了一下cerr的定義(在iostream中)

__PURE_APPDOMAIN_GLOBAL extern _CRTDATA2 istream cin, *_Ptr_cin;
__PURE_APPDOMAIN_GLOBAL extern _CRTDATA2 ostream cout, *_Ptr_cout;
__PURE_APPDOMAIN_GLOBAL extern _CRTDATA2 ostream cerr, *_Ptr_cerr;
__PURE_APPDOMAIN_GLOBAL extern _CRTDATA2 ostream clog, *_Ptr_clog;
也試圖在除錯時跟蹤cout和cerr的執行過程,但是還是沒有發現這兩者的區分。

這個問題有待深究。mark。

參考