C++ 各类输入输出方式效率比较+输入输出优化[含fread、fwrite版]

我们都知道,C++里的输入输出方式有很多。一般OI比赛里用C的scanf/printf,它们效率高。新手入门用cin/cout,它们简单易用。也可以自己做快速读入/输出,效率更高。下面我们通过效率对比来看看效率差距。

效率对比

输入

输入方式 1 0 6 10^6 106 整数
cin 12.789 sec ⁡ 12.789 \sec 12.789sec
scanf 3.615 sec ⁡ 3.615 \sec 3.615sec
快速读入(输入优化) 2.025 sec ⁡ 2.025\sec 2.025sec
生成随机整数函数代码
inline void create(){
	srand(time(NULL));
	FILE *fp=fopen("in.txt","w");
	register int i=1e7;
	while(i--)
		fprintf(fp,"%d\n",rand()%static_cast<int>(1e7));
	fclose(fp);
}
完整测试代码
#include<iostream>
using namespace std;
#include<ctime>
#include<cstdio>
#include<cstdlib>
inline int R(){
	register int x=0;register char c=getchar(),f=1;
	for(;c<48||c>57;c=getchar())if(c==45)f=-1;
	for(;c>47&&c<58;c=getchar())x=(x<<3)+(x<<1)+(c^48);
	return x*f;
}
const int hint=1e6;
int main(){
//	create();
	srand(time(NULL));
	clock_t beg;
	register int i=hint,n;
	puts("输入速度测试:");
	freopen("in.txt","r",stdin);
	
	beg=clock();
	while(i--)
		cin>>n;
	printf("cin speed: %f sec\n",(clock()*1.0f-beg*1.0f)/static_cast<float>(CLOCKS_PER_SEC));
	i=hint;
	beg=clock();
	while(i--)
		scanf("%d",&n);
	printf("scanf speed: %f sec\n",(clock()*1.0f-beg*1.0f)/static_cast<float>(CLOCKS_PER_SEC));
	i=hint;
	beg=clock();
	while(i--)
		n=R();
	printf("fast_in speed: %f sec\n",(clock()*1.0f-beg*1.0f)/static_cast<float>(CLOCKS_PER_SEC));
	
	return 0;
}
运行结果:
输入速度测试:
cin speed: 12.789000 sec
scanf speed: 3.615000 sec
fast_in speed: 2.025000 sec

输出

输出方式 1 0 6 10^6 106 整数
cout 11.503 sec ⁡ 11.503\sec 11.503sec
printf 3.582 sec ⁡ 3.582\sec 3.582sec
快速输出(输出优化) 1.611 sec ⁡ 1.611\sec 1.611sec
完整测试代码
#include<iostream>
using namespace std;
#include<ctime>
#include<cstdio>
#include<cstdlib>
const int hint=1e6;
int a[hint*10];
inline void create(){
	srand(time(NULL));
	for(register int i=0;i<=hint;i++)
		a[i]=rand()%static_cast<int>(1e7);
}
inline void W(register int x){
	if(x<0)putchar(45),x=-x;
	if(x>9)W(x/10);
	putchar(x%10^48);
}
char s[3][50];
int main(){
	create();
	srand(time(NULL));
	clock_t beg,end;
	register int i=hint;
	freopen("out.txt","w",stdout);
	puts("输出速度测试:");

	beg=clock();
	for(i=0;i<hint;i++)
		cout<<a[i];
	end=clock();
	sprintf(s[0],"cout speed: %f sec\n",(end*1.0f-beg*1.0f)/static_cast<float>(CLOCKS_PER_SEC));
	
	i=hint;
	beg=clock();
	for(i=0;i<hint;i++)
		printf("%d",a[i]);
	end=clock();
	sprintf(s[1],"printf speed: %f sec\n",(end*1.0f-beg*1.0f)/static_cast<float>(CLOCKS_PER_SEC));
	
	i=hint;
	beg=clock();
	for(i=0;i<hint;i++)
		W(a[i]);
	end=clock();
	sprintf(s[2],"fast_out speed: %f sec\n",(end*1.0f-beg*1.0f)/static_cast<float>(CLOCKS_PER_SEC));
	
	FILE *fp=fopen("ans.txt","w");
	for(i=0;i<3;i++)
		fputs(s[i],fp);
	fclose(fp);
	return 0;
}
运行结果

打开ans.txt,可以看到:

cout speed: 11.503000 sec
printf speed: 3.582000 sec
fast_out speed: 1.611000 sec

输入/输出优化

可以看到,无论是输入还是输出,三者的效率差距也是惊人。我们可注意到cin和cout的速度非常慢,有人可能说要关闭同步,cin.tie(0),cout.tie(0),我又去测试了一下,但开了这些后,虽能提速,却还是没有scanf/printf快。那么,比它们更快的 快读/写 怎么实现呢?

首先得明白一点,用getchar/putchar比scanf/printf快。但getchar/putchar是针对字符的,整数怎么用getchar/putchar呢?

以下代码均针对整数,由于实数的 快读/写 不常用且效率低,这里不谈。

思路

整数的范围:ASCII码 [ 48 , 57 ] [48,57] [48,57]

输入

可以先循环跳过非整数字符(空格),再用getchar“组装”成字符。

代码
inline int read(){
	register int x=0;register char c=getchar(); // 用register提高效率
	for(;c<48||c>57;c=getchar()); // 跳过非整数字符
	for(;c>47&&c<58;c=getchar())x=(x<<3)+(x<<1)+(c^48); /*
	                            x=x*10+(c^48);
	                            x=x*10+(c-48);
	                            三者是等效的,用位运算("<<",">>","^","|","&","~")
	                            可以加快速度,提高效率,具体规则可以参考
	                            https://baike.baidu.com/item/%E4%BD%8D%E8%BF%90%E7%AE%97/6888804?fr=aladdin
	                         */
	return x;
}

直接赋值版:

inline void read(register int &x){
	x=0;register char c=getchar(); // x=0很重要!
	for(;c<48||c>57;c=getchar());
	for(;c>47&&c<58;c=getchar())x=(x<<3)+(x<<1)+(c^48);
}

输出

可以利用 栈/递归函数 来输出整数,一般先把数分解成若干位。

代码

这里提供递归版本:

inline void write(register int x){
	if(x>9)write(x/10);
	putchar(x%10^48); // 同理,用位运算加快速度
}

负数情况

如果遇到了负数,可以先检测负号(ASCII码:45)。

输入

inline int read(){
	register int x=0;register char c=getchar(),f=1;
	for(;c<48||c>57;c=getchar())if(c==45)f=-1;
	for(;c>47&&c<58;c=getchar())x=(x<<3)+(x<<1)+(c^48);
	return x*f;
}
inline int read(register int &x){
	x=0;register char c=getchar(),f=1;
	for(;c<48||c>57;c=getchar())if(c==45)f=-1;
	for(;c>47&&c<58;c=getchar())x=(x<<3)+(x<<1)+(c^48);
	x*=f;
}

输出

inline void write(register int x){
	if(x<0)putchar(45),x=-x;
	if(x>9)write(x/10);
	putchar(x%10^48);
}

文件输入输出(fread、fwrite)版

如果你不满足 速读/写 的速度怎么办?没关系,还有更快的文件(fread、fwrite)版! fread/fwrite 是一种比 getchar/putchar 还快的输入输出函数,这里涉及到文件流、指针等知识,不了解可以参考:
fread_百度百科
fwrite_百度百科

文件方式暂时没有负数版本。

输入

#define getc() (_b1==_b2?fread(_b,1,100000,stdin),_b2=_b+100000,*((_b1=_b)++):*(_b1++))
char _b[100000],*_b1,*_b2;
inline void read(register int &x){
	x=0;register char c=getc();
	for(;c<48||c>57;c=getc());
	for(;c>47&&c<58;c=getc())x=(x<<3)+(x<<1)+(c^48);
}

输出

char _d[100000],*_p=_d;
inline void write(register int x){
	static int _q[35];register char _t=0; // _q 是一个栈
	do{_q[_t++]=x%10,x/=10;}while(x);
	while(_t){
		register char c=_q[--_t]+48;
		if(_p-_d==100000)fwrite(_d,1,100000,stdout),_p=_d;
		*_p++=c;
	}
}

输出记得在main函数结尾加上:

fwrite(_d,1,_p-_d,stdout);

效率

将文件输入套进上面的测试代码,得到的结果是:

输入速度测试:
cin speed: 12.689000 sec
scanf speed: 3.082000 sec
fast_in speed: 0.174000 sec

ans.txt:

cout speed: 11.681000 sec
printf speed: 2.916000 sec
fast_out speed: 0.275000 sec

可以发现,fread/fwrite 的速度惊人, 1 0 6 10^6 106的整数数据竟快了10倍!普通 速读/写 在它面前只能低头

模板应用(fread、fwrite)

假如数据不是int,是 short、long long 呢?我们可以应用C++里的模板技术,来对函数引入一个通用类型。具体可参考:
模板类_百度百科
而且,如果要一次性输入多个数,还可以使用可变参数模板

代码

#define getc() (_b1==_b2?fread(_b,1,100000,stdin),_b2=_b+100000,*((_b1=_b)++):*(_b1++))
char _b[100000],*_b1,*_b2,_d[100000],*_p=_d;
template<class T>inline void read(register T&x){
	x=0;register char c=getc();
	for(;c<48||c>57;c=getc());
	for(;c>47&&c<58;c=getc())x=(x<<3)+(x<<1)+(c^48);
}
// 可变参数模板
template<class T1,class ...T2>inline void read(register T1&x,register T2&...X){
	read(x),read(X...);
}
template<class T>inline void write(register T x){
	static T _q[35];register char _t=0;
	do{_q[_t++]=x%10,x/=10;}while(x);
	while(_t){
		register char c=_q[--_t]+48;
		if(_p-_d==100000)fwrite(_d,1,100000,stdout),_p=_d;
		*_p++=c;
	}
}

输出还是要记得加:

fwrite(_d,1,_p-_d,stdout);
  • 7
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
C语言中的文件输入输出可以通过标准库中的函数来实现。常用的函数有fopen、fclose、freadfwrite、fgets、fputs等。 首先,可以使用fopen函数打开一个文件,语法如下: ```c FILE *fopen(const char *filename, const char *mode); ``` 其中filename是要打开的文件名(包括路径)。mode是以何种方式打开文件,常用的模式有: - "r":只读方式打开文件,文件必须存在; - "w":写入方式打开文件,如果文件不存在则创建新文件,如果文件已存在则清空文件内容; - "a":追加方式打开文件,如果文件不存在则创建新文件,如果文件已存在则在文件末尾追加内容。 fopen函数返回一个指向FILE结构的指针,用于后续对文件的操作。 接下来,可以使用freadfwrite函数进行二进制输入输出,语法如下: ```c size_t fread(void *ptr, size_t size, size_t count, FILE *stream); size_t fwrite(const void *ptr, size_t size, size_t count, FILE *stream); ``` 其中ptr是存储数据的缓冲区地址,size是每个数据元素的大小(单位为字节),count是要读取/写入的数据元素个数,stream是要进行操作的文件指针。 另外,可以使用fgets和fputs函数进行文本输入输出,语法如下: ```c char *fgets(char *str, int n, FILE *stream); int fputs(const char *str, FILE *stream); ``` 其中str是存储数据的字符数组地址,n是最大读取/写入字符数(包括换行符),stream是要进行操作的文件指针。 最后,使用fclose函数关闭文件,语法如下: ```c int fclose(FILE *stream); ``` 其中stream是要关闭的文件指针。 需要注意的是,在进行文件操作时,应该先判断文件是否成功打开。可以通过判断fopen函数的返回值是否为NULL来确定文件是否成功打开。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值