Linux进度条实现
- 1.\r\n
- 2.缓冲区
- 3.缓冲区分类
- 4.进度条实现
🌟🌟hello,各位读者大大们你们好呀🌟🌟
🚀🚀系列专栏:【Linux的学习】
📝📝本篇内容:\r\n,缓冲区;缓冲区分类;进度条实现
⬆⬆⬆⬆上一篇:循环队列(C语言版)
💖💖作者简介:轩情吖,请多多指教(> •̀֊•́ ) ̖́-
1.\r\n
在我们将进度条之前,我们先来谈谈我们以前学习的换行和回车,了解了这个才能更好的完成进度条。
在我们的以前的学习中,认为\n就是简单的换行,即如下图所示
但是不知道同学们有没有发现一个问题,那就是其实换行,不只是简单的到了下一行,而是到了下一行并且回到了开头,从底层的角度来说,这是回车\t的作用。但是我们在使用的过程中\n就完成了这两步骤,即换行+回车,这是语言的功劳,看下面的演示
可以发现我们的\n直接换行+回车的功能
2.缓冲区
接下来我们来看一下单纯的\r的作用
我们发现程序运行下来好像没有任何的效果,这是为什么?
其实是因为我们的\t是回车,让光标回到了开头,我们的程序执行的又很快,执行完后又需要显示出命令行,因此直接把打印出来的东西给覆盖了。我们来看一下不带回车\t的情况
可以看到啥都不带的情况下,命令行直接在打印内容后面显示了,前面带上\t就是让光标回到了开头,从头开始显示,所以说导致了前面一个程序啥都没显示,这就能更好的理解了。接下来我们设置一个睡眠时间,来更好的看清楚它的执行过程
我们这个时候发现怎么还是没有显示出来?这就非常奇怪了
这就不得不来讲一下缓冲区,我们的printf()中的内容不是说会直接显示在显示器上的,而是先放到缓冲区中,缓冲区会根据特定的规则进行刷新。我们对于显示器的刷新是行刷新,因此我们并没有进行换行什么的,就只能到程序结束时才会刷新了。不过我们可以使用fflush函数来进行主动刷新缓冲区
和我们之前说的一样,在睡眠的5秒期间,光标一直显示在开头,因此后续显示命令行就直接覆盖了
我们再来看一下,使用换行的情况下,睡眠期间能否打印出来
可以看到如果是带了\n,那就达到了显示器的行缓冲区的要求,即使不使用fflush也能立马打印出来
那有的同学会问,如果啥都不带会是什么情况呢?答案是会到程序结束再打印出来,我们来看下面的演示
这样的结果是因为虽然并没有换行符来使内容立马打印出来,但是程序结束前,也需要刷新一下缓冲区。
注意:windows下的情况不一定一样,不同的平台对缓冲区的处理不同,在VS下即使没有\n,也能立马打印出来,对于这一点我认为是图形化界面就是为了可视,所以说就直接显示出来了,无需等待
#include <stdio.h>
#include <Windows.h>
int main()
{printf("hello world");Sleep(10000);//毫秒return 0;
}
3.缓冲区分类
缓冲区分为三种类型:全缓冲、行缓冲和不带缓冲。
全缓冲:在这种情况下,当填满标准I/O缓存后才进行实际I/O操作。全缓冲的典型代表是对磁盘文件的读写。
行缓冲:在这种情况下,当在输入和输出中遇到换行符时,执行真正的I/O操作。这时,我们输入的字符先存放在缓冲区,等按下回车键换行时才进行实际的I/O操作。典型代表是标准输入(stdin)和标准输出(stdout)。
不带缓冲:也就是不进行缓冲,标准出错情况stderr是典型代表,这使得出错信息可以直接尽快地显示出来。
接下来验证一下不带缓冲区和全缓冲区,行缓冲区前面已经讲过了
全缓冲区验证:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main()
{FILE *fp = fopen("./log.txt", "a");int i = 0;char str[]="hello";while (i <= 100000){fwrite(str,strlen(str),1,fp);usleep(10000);//0.01秒,usleep()单位是微秒i++;}while(1);return 0;
}
可以看到我们执行了代码后,右边的显示log.txt操作持续了好几次都没有显示东西出来,过了一会就出现了,这就证明了只有当缓冲区满了后才会刷盘(测试时如果要清空log.txt可以使用>log.txt重定向)
但是这只是语言层面的,在操作系统的系统调用中的IO不是这样的,它有自己的策略
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <cstring>
int main()
{umask(0);int fd = open("./log.txt",O_WRONLY|O_CREAT,0666);//使用系统的IOint i = 0;char str[]="hello";while (i <= 100000){write(fd,str,strlen(str));usleep(10000);//0.01秒,usleep()单位是微秒i++;}// while(1);//保证不是因为程序退出而写入的return 0;
}
不带缓冲区验证:
#include <stdio.h>
#include <unistd.h>
int main()
{fprintf(stderr,"hello world");sleep(5);return 0;
}
4.进度条实现
我们要实现的进度条是这样的,如上图
先来看一下第一个版本
// main.cc
#include "progress.hpp"
#include <unistd.h>
int main()
{Run();return 0;
}
//progress.cc
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#define BUFF_SIZE 101
#define STYLE '#'
void Run()
{// 1.先定义一个缓冲区来存储进度条char buff[BUFF_SIZE]; // 需要101一个的原因是最后要添加\0// 清空,保证全为0,后续就不需要在进行修改memset(buff, 0, sizeof(buff));//2.设置动态的标志,保证进度条在运行char lable[5]="/|-\\";// 3.显示进度条int i = 0;while (i <= 100){//-100为了保证左对齐,同时剩余位置添加空格,/r是使光标回到开头,覆盖掉上一次的进度条printf("[%-100s][%d%%][%c]\r", buff, i,lable[i%4]);fflush(stdout);usleep(100000);//0.1秒,每0.1秒显示一下进度条,每次进度条都会变化buff[i++]=STYLE;}printf("\n");}
//progress.hpp
#include <iostream>
#include <stdio.h>
#include <string.h>
void Run();
显示效果:
来看一下第二个版本
//progress.cc
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#define BUFF_SIZE 102 //102是为了使最后的>能够存储,不至于越界
#define STYLE '='
#define ARR '>'
void Run()
{// 1.先定义一个缓冲区来存储进度条char buff[BUFF_SIZE]; // 需要101一个的原因是最后要添加\0// 清空,保证全为0,后续就不需要在进行修改memset(buff, 0, sizeof(buff));//2.设置动态的标志,保证进度条在运行char lable[5]="/|-\\";// 3.显示进度条int i = 0;while (i <= 100){//-100为了保证左对齐,同时剩余位置添加空格,/r是使光标回到开头,覆盖掉上一次的进度条printf("[%-100s][%d%%][%c]\r", buff, i,lable[i%4]);fflush(stdout);//刷新一下缓冲区,不然会无法正常显示出来usleep(100000);//0.1秒,每0.1秒显示一下进度条,每次进度条都会变化buff[i++]=STYLE;//if(i>100)也可以这样写,就可以不用把BUFF_SIZE设为102,101即可if(i!=100)buff[i]=ARR;//把最后一个显示为>,更加形象}printf("\n");}
这个版本仅仅是让进度条更好看点,如果大家愿意也可以添加颜色,作者就不在这演示了,防止大家在看代码时迷糊
🌸🌸Linux进度条实现的知识大概就讲到这里啦,博主后续会继续更新更多C++的相关知识,干货满满,如果觉得博主写的还不错的话,希望各位小伙伴不要吝啬手中的三连哦!你们的支持是博主坚持创作的动力!💪💪