C++标准流重定向及cout和cerr的区别

前言

当我们把一个win32控制台的程序重构成界面程序的时候,之前的所有cout输出语句就变得没用了,而不得不重新查找替换成其他输出显示方式。或者要输出大量信息的时候,想要看某一步的输出,却很快地被新的输出覆盖了(尤其在输出窗口的缓冲区设得比较小的时候)。在这些情况下,如果能快捷地将输入输出流重定向到文件中,一切就显得迎刃而解了。

正文

一、C++标准输入输出流的重定向

C++的标准输出流cout默认是输出到显示设备中,标准输入流cin默认是从键盘中读取数据。而在很多情况下,打印到屏幕的信息太多而来不及查看,我们想把输出的东西保存到文件中,以便后续的查看分析。利用cout的重定向就可以在输出到屏幕和输出到文件之间轻松切换。

1、利用cmd

1
2
3
4
5
6
7
8
9
10
11
#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,转到程序目录下,执行命令:

1
TestStream < input.txt > output.txt

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

Input.txt存放的是要输入的字符串,需要实现新建好。output.txt是输出文件,会自动生成。运行后就把字符输出到文件中,而不打印到屏幕。

2、利用rdbuf函数

上面是利用cmd把cout和cin重定向到文件中的方法,但是在利用visual studio开发的时候,总是利用cmd运行程序就显得很不方便,此时的解决方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#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 error state flags.

(更详细介绍请参看http://www.cplusplus.com/reference/ios/ios/rdbuf/

cout.rdbuf(fout.rdbuf())这个语句的意思就是把cout的流缓冲区设置成ofstream的流缓冲区,这样cout所输出的信息都被输出到文件中。

代码中最后两句话是为了把标准输入输出流的缓冲区恢复成原有设置。

对于C形式的printf和scanf,上述设置并不起作用,可以用一下设置完成:

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

二、cout和cerr的区别

1、cout对应于标准输出流

cerr对应于标准错误流

2、cout和cerr的主要区别就是cout可以利用cmd命令行参数的方式进行重定向,而cerr则不行。

1
2
3
4
5
6
7
8
9
10
11
#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方法重定向到文件中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#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中)

1
2
3
4
__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;</pre>

也试图在调试时跟踪cout和cerr的执行过程,但是还是没有发现这两者的区分。

这个问题有待深究。mark。

参考

http://www.cnblogs.com/kex1n/archive/2012/01/06/2314985.html
http://blog.csdn.net/hjs1122/article/details/6127712
http://blog.csdn.net/yclz/article/details/1570952