前言
使用c++一段时间后,想从某data.input文件中读入数据,而不是从小黑窗里键盘输入。
或者想直接输出到某个data.txt文件,而不是打印在小黑窗里,就需要用到文件重定向。
本文主要介绍3种方案。
C++中可以rdbuf()方案。
C中可以用freopen(),和 {fscanf,fprintf},两种方案。
顺带一些坑。
①C++ 实现方法
用好iostream里的cin.rdbuf()和cout.rdbuf函数,
以及ftream里的in和out两个rdbuf()函数。
1.1 无参数时,4个函数的返回值相同,都是各自buffer的指针
//istream和ostream的无参数rdbuf()
_Mysb * rdbuf() const
{ // return stream buffer pointer
return (_Mystrbuf);
}
//ifstream和ofstream的无参数rdbuf()
_Myfb *rdbuf() const
{ // return pointer to file buffer
return ((_Myfb *)&_Filebuffer);
}
1.2 cin.rdbuf()和cout.rdbuf有含参的重载形式
效果为设置传入的参数指针为自己的buffer指针,返回原buffer指针。
_Mysb *rdbuf(_Mysb *_Strbuf)
{ // set stream buffer pointer
_Mysb *_Oldstrbuf = _Mystrbuf;
_Mystrbuf = _Strbuf;
clear();
return (_Oldstrbuf);
}
1.3 C++实现案例
下面代码设置的fout默认是"w"模式,每次运行时,cout的输出会覆盖原内容。
也就是说无论运行下面代码多少遍获得的output.txt内容完全一致。
output.txt不存在时会自动创建文件。
想变成"rw"追加模式,只需要修改为fout("output.txt",ios::app);即可。
#include <iostream>
//#include <ostream>
#include <fstream>
using namespace std;
int main()
{
ifstream fin("input.txt"); // 输入文件
ofstream fout("output.txt"); //输出文件
streambuf *cinbackup;
streambuf *coutbackup;
cinbackup = cin.rdbuf(fin.rdbuf()); //用 rdbuf() 重新定向,返回旧缓冲区指针
coutbackup = cout.rdbuf(fout.rdbuf()); //用 rdbuf() 重新定向,返回旧缓冲区指针
char a[100];
cin >> a; //从input.txt文件读入
cout << a << endl; //写入 output.txt
//还原标准输入输出流
cin.rdbuf(cinbackup); // 取消,恢复键盘输入
cout.rdbuf(coutbackup); //取消,恢复屏幕输出
fin.close();//随手关闭
fout.close();//是好习惯
return 0;
}
②C语言版本
2.1使用freopen()函数
其声明为
FILE *freopen( const char *filename, const char *mode, FILE *stream );
代码示例1
#include<stdio.h>
int main(){
freopen("input.txt","r",stdin);
freopen("output.txt","w",stdout);
int a,b;
scanf("%d %d",&a,&b);
printf("%d\n",a+b);
fclose(stdin);//关闭文件
fclose(stdout);//关闭文件
}
//如果想改回来,需要重新打开标准控制台设备文件,这个跟系统有关
//Windows系统下
freopen("CON", "r", stdin);
freopen("CON", "w", stdout);
//Linux系统下
freopen("/dev/console", "r", stdin);
freopen("/dev/console", "w", stdout);
//参考https://blog.csdn.net/qingchenwuhui/article/details/46456469
2.2使用fscanf、fprintf
需要先定义相关的FILE*指针
代码示例2
#include<iostream>
#include<fstream>
int main()
{
//重定向输入
FILE *in = fopen("input.txt", "r");
fscanf(in, "%d %d", &num, &wNum);
//重定向输出
FILE *out = fopen("output.txt", "w");
fprintf(out, "%.1lf", avg);
//随手关闭,是好习惯
fclose(in);
fclose(out);
return 0;
}
③常见的坑
3.1 fsprintf无效怎么办
【描述】函数fprintf使用之后,文件内容为空,输出没有被写入文件中。
【问题解析】
这是由于C中存在一种“缓存机制”。
为了减轻系统的实时I/O负担,使用fopen以"w“打开的文件,会默认分配一个buf缓冲区。
缓冲区的大小由系统头文件<stdio.h>中的BUFSIZ定义。
/* Buffered I/O macros */
#define BUFSIZ 512
//以上摘自Windows SDK 10.0.17134.0中的stdio.h头文件
直到buf缓冲区被填满,才会进行一次真正的写入操作,清空缓冲区;
=>或者程序员直接调用fflush()主动清空缓冲区,将内容写入文件;
=>或者使用fclose()结束一个stream的生命周期时,由函数自动清空缓冲区;
=>或者main()函数生命周期结束时,由系统自动清空缓冲区。
所以我们有2种比较合适的方式解决这个问题。
3.1.1 取消缓存机制,使用setbuf()函数
void setbuf(FILE* _Stream, char* _Buffer)
将fopen时自动分配的buf替换成我们指定的buf
如果指定的_Buffer = NULL,
则缓存机制失效,实时进行写入操作
代码示例
FILE *out = fopen("output.txt", "w");
setbuf(out,NULL);
fprintf(out, "%d", n);
3.1.2 主动清空缓冲区,使用fflush()函数
int fflush(FILE *stream);
代码示例
FILE *out = fopen("output.txt", "w");
fprintf(out, "%d", n);
fflush(out); //清空out的缓冲区,立即进行一次写操作
//还有一个fflushall(),无需参数,对所有已打开文件进行一次主动清空
一些关于fflush的补充
If the given stream was open for writing (or if it was open for updating and the last i/o operation was an output operation) any unwritten data in its output buffer is written to the file.
如果stream指向一个输出流,或者指向一个最后一次I/O操作是输出的更新流,那么fflush函数将把buffer内所有未处理的数据写入stream对应的文件。
If stream is a null pointer, all such streams are flushed.如果我们设置参数为NULL,上述所有流都将被刷新。
注意,其他情况下,fflush(NULL)的行为是不确定的,与不同的编译器有关。
比如fflush(stdin ),stdin是一个输入流,不满足第一段说的open for writing or updating,因此有的编译器支持,有的编译器不支持,不可预料结果。
The stream remains open after this call
fflush不会关闭流。
A zero value indicates success.
If an error occurs, EOF is returned and the error indicator is set (see ferror).如果正确处理,返回0。
如果发生写错误,fflush函数会给出错的流打上错误标记,并且返回EOF。
//setbuf与缓冲区详解请参考https://blog.csdn.net/zhoubl668/article/details/7076324
//关于fflush()参考http://www.cplusplus.com/reference/cstdio/fflush/