论坛的lhslktg朋友发了
一个贴,大意是说在他的程序里面调用了很多的cout的输出,是否能够使用最快速的方法,使得程序的输出能够定向到一个文件内。我理解这个所谓的快速的方法,就是尽量不要改动原有的程序,至少不要改动程序的内部,而达到这个功能。
有朋友给了一个最好的办法,就是命令输出重定位。假如,应用程序的名称为: testcmd,则可以使用下面的命令:
testcmd >test.log
就把命令中的cout的输出写到文件中去了。
还有的朋友给出了在程序开始增加一句标准输出重打开的语句:
freopen("test.log", "w", stdout)
这样也会把这个语句下面的所有输出都写到test.log文件中去了。
但接着这位朋友又有了新的要求,想既保留原有屏幕的输出,又能写到文件中去。
我给出了下面的方案。
testcmd | tee test.log
这里,tee是UNIX系统中的命令,它就像它的名字一样,充当管道T型接头。将输出分成两个流,一个到test.log中去了,另一个仍然输出到屏幕上去。接着,我给
出了tee的简单的源码实现,是为了在那些没有tee命令的系统上使用的。源码如下:
# include <stdio.h>
int
main(int argc, char **argv)
{
FILE *fp;
int c;
if ( argc > 1 ) {
fp = fopen(argv[1], "w");
if ( fp == NULL ) {
fprintf(stderr, "%s: can not open <%s>/n", argv[0], argv[1]);
return -1;
}
} else
fp = stdout;
while ( (c = fgetc(fp)) != EOF )
fputc(fp);
return 0;
}
不料,不知道是不是楼主没有仔细看我的说明,竟然问我怎样使用这段代码。我感觉,上面应该说明的很清楚了,就是把这段程序编译成可执行文件,命令名字叫做tee,或者tee.exe, 然后像上面介绍的那样使用:
testcmd | tee test.log
就把testcmd程序的输出既输出到文件里了,又输出到屏幕上去了。不知道这样说,是否明白了。
到此为止,事情还没完,又有了新的需求。就是说,在原来的程序里,既有cout的输出,又有printf的输出。能不能单独控制这两输出分别到屏幕和不同的文件中去。比如将cout的输出到cout.txt,而将printf输出到printf.out中去。我在跟贴里,给出了控制cout的方法,就是在程序的开始,增加下面的代码:
1 # include <iostream>
2 # include <fstream>
3
4 class dostream : public std::ostream {
5 public:
6 dostream() : ofs("cout.txt") {}
7
8 template <typename _T>
9 dostream& operator << (_T& data) {
10 std::cout << data;
11 ofs << data;
12
13 return *this;
14 }
15
16 private:
17 std::ofstream ofs;
18 };
19
20 dostream dout;
21
22 # define cout dout
楼主继续发问:
这个是如何控制cout的输出的?
直接在程序最开始添加会有什么效果呢?
好了,我现在开始解释这段程序:
这里,从第4行开始,设计了dostream类,并继承了ostream类,也就是说它继承了ostream的所有函数功能,别忘了,cout就是ostream的子类对象啊!这个类很简单,就是在构造函数里,打开了一个输出文件cout.txt。既然是要控制cout的输出,也就是要控制cout的<<操作符。所以,从第8行开始定义了一个重载<<的模版函数,因为要对各种类型进行输出重载,所以这里的输出数据类型成为了模版参数。在这个模版函数里面,第10行是按照正常的cout的输出,输出到屏幕上,而第11行则是将输出输出到cout.txt文件中去了。
第20行,定义了dostream的一个对象 dout。
第22行,将cout定义为dout,这样当预处理的时候,会将程序中所有调用cout的地方都替换为dout,也就是都调用了我们新定义的这个dostream类的功能。而这个
主要动能就表现在<<操作符的重载上,从而调用了我们实现的重载的函数,把输出写到屏幕上,也输出到文件中去了。
如果还不明白,我们就从实际的例子来说明。
假如,原来的程序里有一句:
cout << "Hello/n";
当把上面的那段程序包含在程序的开始的时候,就出现下面的情况:
在预处理的时候,会根据上面程序的22行进行宏替换,这样,cout >> "Hello/n" 便被替换为:
dout << "Hello/n";
当编译的时候,这个语句便会使用上面第8行开始定义的模版函数,模版的参数为string,也就是会调用下面的函数:
dostream& operator << (string& data);
这里 data = "Hello/n", 也就是说 dostream& operator << ("Hello");
从而调用10行和11行,就把"Hello"分别输出到屏幕上和文件中了。
另外,下面再给出控制printf输出的方法。类似的给出下面的代码:
# include <stdio.h>
# include <stdarg.h>
FILE *ofp;
void
init_printf(void)
{
ofp = fopen("printf.txt", "w");
if ( ofp == NULL ) {
fputs("can not open <printf.txt>/n", stderr);
exit(-1);
}
}
int
printf(const char *format, ...)
{
va_list ap;
int rc;
va_start(ap, format);
vprintf(format, ap);
rc = vfprintf(ofp, ap, format);
va_end(ap);
return rc;
}
这段代码可以单独编辑为一个源程序文件,比如,printf.c。在原来的程序的开始,增加一句:
init_printf()
然后,使用下面的命令进行编译链接:
cc -o testcmd testcmd.cpp printf.c
testcmd.cpp假设为原来的程序。这样,程序会优先调用printf.c中的函数,也就是我们在上面编写的那个printf,而不会调用标准库里的printf了,也就实现了既输出到屏幕上也输出到文件里的功能了。上面的程序调用了C语言标准库的关于变参函数的方法和技巧,如果不太明白,请参考相关书籍。
有朋友给了一个最好的办法,就是命令输出重定位。假如,应用程序的名称为: testcmd,则可以使用下面的命令:
testcmd >test.log
就把命令中的cout的输出写到文件中去了。
还有的朋友给出了在程序开始增加一句标准输出重打开的语句:
freopen("test.log", "w", stdout)
这样也会把这个语句下面的所有输出都写到test.log文件中去了。
但接着这位朋友又有了新的要求,想既保留原有屏幕的输出,又能写到文件中去。
我给出了下面的方案。
testcmd | tee test.log
这里,tee是UNIX系统中的命令,它就像它的名字一样,充当管道T型接头。将输出分成两个流,一个到test.log中去了,另一个仍然输出到屏幕上去。接着,我给
出了tee的简单的源码实现,是为了在那些没有tee命令的系统上使用的。源码如下:
# include <stdio.h>
int
main(int argc, char **argv)
{
FILE *fp;
int c;
if ( argc > 1 ) {
fp = fopen(argv[1], "w");
if ( fp == NULL ) {
fprintf(stderr, "%s: can not open <%s>/n", argv[0], argv[1]);
return -1;
}
} else
fp = stdout;
while ( (c = fgetc(fp)) != EOF )
fputc(fp);
return 0;
}
不料,不知道是不是楼主没有仔细看我的说明,竟然问我怎样使用这段代码。我感觉,上面应该说明的很清楚了,就是把这段程序编译成可执行文件,命令名字叫做tee,或者tee.exe, 然后像上面介绍的那样使用:
testcmd | tee test.log
就把testcmd程序的输出既输出到文件里了,又输出到屏幕上去了。不知道这样说,是否明白了。
到此为止,事情还没完,又有了新的需求。就是说,在原来的程序里,既有cout的输出,又有printf的输出。能不能单独控制这两输出分别到屏幕和不同的文件中去。比如将cout的输出到cout.txt,而将printf输出到printf.out中去。我在跟贴里,给出了控制cout的方法,就是在程序的开始,增加下面的代码:
1 # include <iostream>
2 # include <fstream>
3
4 class dostream : public std::ostream {
5 public:
6 dostream() : ofs("cout.txt") {}
7
8 template <typename _T>
9 dostream& operator << (_T& data) {
10 std::cout << data;
11 ofs << data;
12
13 return *this;
14 }
15
16 private:
17 std::ofstream ofs;
18 };
19
20 dostream dout;
21
22 # define cout dout
楼主继续发问:
这个是如何控制cout的输出的?
直接在程序最开始添加会有什么效果呢?
好了,我现在开始解释这段程序:
这里,从第4行开始,设计了dostream类,并继承了ostream类,也就是说它继承了ostream的所有函数功能,别忘了,cout就是ostream的子类对象啊!这个类很简单,就是在构造函数里,打开了一个输出文件cout.txt。既然是要控制cout的输出,也就是要控制cout的<<操作符。所以,从第8行开始定义了一个重载<<的模版函数,因为要对各种类型进行输出重载,所以这里的输出数据类型成为了模版参数。在这个模版函数里面,第10行是按照正常的cout的输出,输出到屏幕上,而第11行则是将输出输出到cout.txt文件中去了。
第20行,定义了dostream的一个对象 dout。
第22行,将cout定义为dout,这样当预处理的时候,会将程序中所有调用cout的地方都替换为dout,也就是都调用了我们新定义的这个dostream类的功能。而这个
主要动能就表现在<<操作符的重载上,从而调用了我们实现的重载的函数,把输出写到屏幕上,也输出到文件中去了。
如果还不明白,我们就从实际的例子来说明。
假如,原来的程序里有一句:
cout << "Hello/n";
当把上面的那段程序包含在程序的开始的时候,就出现下面的情况:
在预处理的时候,会根据上面程序的22行进行宏替换,这样,cout >> "Hello/n" 便被替换为:
dout << "Hello/n";
当编译的时候,这个语句便会使用上面第8行开始定义的模版函数,模版的参数为string,也就是会调用下面的函数:
dostream& operator << (string& data);
这里 data = "Hello/n", 也就是说 dostream& operator << ("Hello");
从而调用10行和11行,就把"Hello"分别输出到屏幕上和文件中了。
另外,下面再给出控制printf输出的方法。类似的给出下面的代码:
# include <stdio.h>
# include <stdarg.h>
FILE *ofp;
void
init_printf(void)
{
ofp = fopen("printf.txt", "w");
if ( ofp == NULL ) {
fputs("can not open <printf.txt>/n", stderr);
exit(-1);
}
}
int
printf(const char *format, ...)
{
va_list ap;
int rc;
va_start(ap, format);
vprintf(format, ap);
rc = vfprintf(ofp, ap, format);
va_end(ap);
return rc;
}
这段代码可以单独编辑为一个源程序文件,比如,printf.c。在原来的程序的开始,增加一句:
init_printf()
然后,使用下面的命令进行编译链接:
cc -o testcmd testcmd.cpp printf.c
testcmd.cpp假设为原来的程序。这样,程序会优先调用printf.c中的函数,也就是我们在上面编写的那个printf,而不会调用标准库里的printf了,也就实现了既输出到屏幕上也输出到文件里的功能了。上面的程序调用了C语言标准库的关于变参函数的方法和技巧,如果不太明白,请参考相关书籍。