C语言到C++STL菜鸟教程:如何加速输入输出效率

2022-07-1713:07:42编程语言入门到精通Comments1,135 views字数 3889阅读模式

1. 简介文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/25129.html

遇到大数据时,往往读写文件成了程序运行速度的瓶颈,需要更快的读取方式。相信几乎所有的C++学习者都在cin机器缓慢的速度上栽过跟头,有很多案例中提供几个数据,却在后台测评却提供了近千,近万的数据量是常事,而很多人会发现,明明算法正确的问题,却总是在超时,但把自己的输入换成scanf,输出换成printf之后莫名其妙又可以通过了,于是便冷眼相对C++的cin与cout。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/25129.html

在C++中,cin与cout往往不需要我们手动设置格式而变得灵活,因此更趋向于我们便捷式的使用,但这并不是说cin与cout就一定比scanf和printf慢,我们可以通过C++输入输出流解除绑定的方式进行加速,使其提升至C语言scanf和printf般的速度。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/25129.html

2.原理:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/25129.html

cin在为了与scanf保持同步,设置了一个缓冲区,为了保证各位混用两者的情况不会出错,利用这个缓冲区进行同步,不至于发生指针错误造成乱码,因此cin会牺牲一点点效率,而这一点点的效率,在大数据读取和运算的时候也会产生极大的影响,我们可以通过sync_with_stdio(false)的方式取消这个缓冲区,让cin变成和scanf一样的效率。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/25129.html

a) sync_with_stdio文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/25129.html

这个函数是一个“是否兼容stdio”的开关,C++为了兼容C,保证程序在使用了std::printf和std::cout的时候不发生混乱,将输出流绑到了一起,默认情况为sync_with_stdio(ftrue),即开启。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/25129.html

b)cin.tie(0),cout.tie(0);文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/25129.html

cin.tie(NULL);cout.tie(NULL);只解除的是C++运行库层面的对数据传输的绑定,stdin和stdout应该在更底层的操作系统层面有绑定,没有解除,也就是说,cin.tie(0)与cout.tie(0)的方式是继续松绑c++传输的效率。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/25129.html

因此我们可以在自己的代码中建立一个如此的模板:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/25129.html

#include<iostream>
using namespace std;
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
 
    /*
        写上你想写入的代码,并使用cin,cout输入输出
     */
 
    return 0;
}

也可以用宏定义的方式简写这段代码:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/25129.html

#define jiasu ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);

在主函数进行引用即可。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/25129.html

根据最近的输出速度测试,分别在不同的平台进行存粹的输出测试,使用解除绑定的cout相对而言是最快的输出方式,而输入则因为某些受限的原因并不是最快的,为了使输入数据变快,请看下一章,快速读取。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/25129.html

备注:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/25129.html

1.NULL在C/C++语言中对应0,因此可以拿0代替NULL(空指针)文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/25129.html

2.相关测试数据:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/25129.html

WINDWOS下[1e5的数据量]:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/25129.html

使用解除绑定的cout : 30.719000 秒 , 29.518000 秒 , 29.446000 秒文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/25129.html

不使用解除绑定cout : 51.749000 秒 , 49.383000 秒 , 47.605000 秒文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/25129.html

C++文件的printf : 84.962000 秒 , 76.131000 秒 , 77.639000 秒文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/25129.html

C语言文件的printf : 29.776000 秒 , 29.327000 秒 , 29.862000 秒文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/25129.html

LINUX系统下[1e5的数据量]:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/25129.html

使用解除绑定的cout : 0.199213 秒 , 0.195920 秒 , 0.195387 秒文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/25129.html

不使用解除绑定cout : 0.199305 秒 , 0.188013 秒 , 0.199603 秒文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/25129.html

C++文件的printf : 0.195575 秒 , 0.197582 秒 , 0.197400 秒文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/25129.html

C语言文件的printf : 0.195144 秒 , 0.200245 秒 , 0.215732 秒文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/25129.html

【在WINDOWS上面运行很久的程序在LINUX全部低于一秒钟结束】文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/25129.html

LINUX系统下[1e7(一百万)的数据量]:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/25129.html

使用解除绑定的cout : 18.359119 秒 , 18.066489 秒 , 18.309879 秒文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/25129.html

不使用解除绑定cout : 18.655116 秒 , 18.655820 秒 , 18.657697 秒文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/25129.html

C++文件的printf : 18.354496 秒 , 18.592422 秒 , 18.312166 秒文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/25129.html

C语言文件的printf : 18.463724 秒 , 18.475845 秒 , 18.495757 秒文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/25129.html

3.简介文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/25129.html

自上一篇文章,我们了解了解除绑定的输入输出流,这会让我们的代码输出变得迅速,然而,对于输入而言,亦有快速读取这一个更优秀的方案(相对来说也较为麻烦)。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/25129.html

在我们学习C语言的时候,我们曾经学过字符的输入函数getchar(),她从标准输入里面读取下一个字符,相当于gets(stdin),返回类型为int型,为用户输入的ASCII码,出错则返回-1。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/25129.html

4.实现方式文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/25129.html

将所读取的数据按位进行分段截取,比如读取入123这个数据,在确定所读取的是数字的情况下,先读取数字1,如果其后还有数据,将第一个读取的数据1先乘以10再读取第二个数据2,接着读取3,将前面的12再乘以10,最后就可以读取成功123,而对于负数而言,我们只需要设置一个flag标记,对第一位的读取进行一个特判即可,在读取完这些数据之后,将获取到的整体数据进行返回,这边就是快读的基本思想。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/25129.html

请记住这个核心:判断是否为数字à按位读取à转换ASCII码为数字à继续读取à进行乘10与相加à读取结束返回存储的值文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/25129.html

这里有一个简单版本的快读文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/25129.html

inline int read()
{
    int X=0; bool flag=1; char ch=getchar();
    while(ch<'0'||ch>'9') {if(ch=='-') flag=0; ch=getchar();}
    while(ch>='0'&&ch<='9') {X=(X<<1)+(X<<3)+ch-'0'; ch=getchar();} 
    if(flag) return X;
    return ~(X-1);
}

使用inline重载函数和重写read(),也可以去掉inline,只不过这样效率稍微慢一些,输入的使用方法就可以照做一般的函数进行使用即可。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/25129.html

 int tmp=read();
    std::cout<<tmp<<std::endl;

5.扩展,快速输出文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/25129.html

通快速读取,亦有快读输出,其思想更为简单,效率也只是略微胜一筹,其思维是,按照每一位进行拆解输出,注意我们需要让在后面的数字后输出,拆解位时就务必要注意,因此,较为简单的方法就是模仿递归进行设计。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/25129.html

inline void write(int X)
{
    if(X<0) {X=~(X-1); putchar('-');}
    if(X>9) write(X/10);
    putchar(X%10+'0');
}

6. 推荐模板文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/25129.html

使用C++的万能模板类并创建新的命名空间IO,可以手动指明所需要返回参数的类型,int还是long还是别的,这样的方式更加人性化,可以省去上面那样简单的方式必须手动修改函数类型的缺点,但是这样的模板却更加的复杂和繁长,不易快速上手使用,权且做一个理解。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/25129.html

#include<iostream>
namespace IO
{
    inline char nc() {
        static char buf[100000], *p1 = buf, *p2 = buf;
        return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1++;
    }
    template<typename T>
    inline T read() {
        char ch = nc(); 
        T sum = 0;
        while (!(ch >= '0'&&ch <= '9'))
        {
            ch = nc();
            if (ch == EOF)  return EOF;
        }
        while (ch >= '0'&&ch <= '9')
        {
            sum = sum * 10 + ch - 48;
            ch = nc();
            if (ch == EOF)  return EOF;
        }
        return sum;
    }
}
using namespace IO;
 
int main(){
    long long tmp=read<long long>();    //输入方式
    std::cout<<tmp<<std::endl;
 
    while(tmp = read<long long>()!=EOF){} //循环判断EOF的方式
    return 0;
}

7.本篇后语文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/25129.html

C++与STL篇就此结束了,本篇在模式上更趋向于杂谈,STL内容何其多,足足要近千页的书才能讲完讲彻底,因此本章只是提出一个引子,一个简单的介绍,具体的内容和内涵需要多多自学完成,而对于快读和解绑的操作,更加趋向于应用在算法竞赛中抢的速度,具体如何搭配使用,也请自主根据数据规模进行决断。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/25129.html

  • 本站内容整理自互联网,仅提供信息存储空间服务,以方便学习之用。如对文章、图片、字体等版权有疑问,请在下方留言,管理员看到后,将第一时间进行处理。
  • 转载请务必保留本文链接:https://www.cainiaoxueyuan.com/ymba/25129.html

Comment

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定