C++中文件重定向详解

前言

使用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/

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值