作者:小树苗渴望变成参天大树
作者宣言:认真写好每一篇博客
作者gitee:gitee
作者专栏:C语言,数据结构初阶,Linux,C++ 动态规划算法
如 果 你 喜 欢 作 者 的 文 章 ,就 给 作 者 点 点 关 注 吧!
大家好久不见,已经快一个月没有更新Linux相关的知识了,希望大家还没有忘记前面学过的知识,不记得可以看看我前面写的博客,可以快速帮大家进行回忆,今天我们讲讲一个新的知识,把我们前面说到的make进行联系起来,再讲小程序进度条之前,我还有两个知识铺垫,不然大家不好理解,话不多说,我们开始进入正文
再讲解进度条之前,我们必须了解这两个概念,一个是回车换行,一个是行缓冲区
对于回车换行其实是两个概念,一个是回车,一个是换行,换行是光标换到下一行,并不会到文本的开头,回车使光标回到当前行的开始,那在我们语言的层面是怎么做到对文本进行换行和回车的,我们平时说到 \n其实就是换行的意思的,但是语言的本身把他解释成回车换行,\r是回车的意思的,回车换行其实就是先换行再进行回车,我们来看看老式的键盘
这个标志就很形象,为什么要讲解这个知识点,原因是为了更好的引出下面这个要铺垫的知识点
再我们的C语言中,我们要行缓冲区,这是个什么,为什么要有这个,再我们运行程序的时候,我们会在屏幕上看到显示的内容,这些都是由字符组成的,那我们的C语言不是一个字符一个字符的显示再屏幕上,这样觉得消耗太大了,所以我们有了行缓冲区的概念,就是程序运行时的一块内存,把数据先放到这个内存里面,等到这个小内存满了,就直接一起全部给刷新出来,为什么又叫行缓冲区,就是当一行满了,就一起刷新出来,只有遇到\n或者scanf或者fflush这样的函数就会提前进行刷新,程序结束后,最终也会将数据进行刷新出来。
所以我们来看一个例子:
#include
#include
int main()
{
printf("hello Makefile!\n");
sleep(3);
return 0;
}
#include
#include
int main()
{
printf("hello Makefile!");//1
sleep(3);//2
return 0;
}
大家看到这个结果大概率会认为先执行了2代码,再执行1代码,但是事实却不是这个样子的,我们C语言的代码都是自上往下进行执行的,除非遇到跳转,循环之类的,对于上面的代码的肯定是先执行的打印函数,再执行休眠函数,那为什么不是先打印出来呢??原因就是,打印的数据再缓冲区里面,然后开始执行了2代码,最后程序结束了,就会一起刷新出来,最后从显示出来。
我们想必须提前刷新出来,还有一种方法就是使用fflush函数,我们查看一下手册
再我们c语言会默认打开三个流,这个再C语言专栏的文件操作讲过,一个是输入流stdin,一个是输出流stdout,一个是错误流stderr,而fflush的参数就是传流进出,这里是将数据输出,所以使用输出流
代码修改:
#include
#include
int main()
{
printf("hello Makefile!");
fflush(stdout);
sleep(3);
return 0;
}
结合上面讲的两个知识点,进行编写一个倒计时。
1 #include"processBar.h"
2 int main()
3
4 {
5 int cnt=9;
6 while(cnt>=0)
7 {
8 printf("%d",cnt);
9 fflush(stdout); //不加会显示不出来,最后会一起显示出来
10 sleep(1);
11 cnt--;
12 }
13 return 0;
14 }
显然这两种都不是我们想要的倒计时,我们看看有一个特点,我们每输出一个时间光标就往后面跑一个字符,所以想要把之前的字符覆盖,就要是光标一直再开头的位置,所以我们再打印的时候加一个 \r
+\r
这才是我们想要的倒计时,但是这个程序还有一个小毛病,但从大于9开始倒计时就会出现问题,我们需要进行打印格式的调整
会出现下面的情况:
到这里我们的知识铺垫到此结束,接下来开始正式讲解进度条的实现
我们最终是要将我们的进度条模拟实现成多软件一起下载的场景,如下:
此次进度条的模拟实现,我打算使用多文件的方式,使用自动化构建来编写,我们开始做一些前期工作
我们准备工作做好之后,我们开始来实现进度条代码吧
设计思想: 在实现对多软件的模拟进度条下载,我们必须先弄清楚一个普通的进度条是怎么实现的吧。我们就需要使用到\r的特性,\r是回车的意思,将光标回到当前行开始的地方,我们采取的是先打印一个字符,然后再增加一个字符进行覆盖打印,如果是换行打印,就类似于一个直角三角形,我们来看代码实例:
错误示范
这个显然不是我们想要的效果,我们达到我们想要的推进式打印,但是想要打印在同一行,这时候就需要使用\r使光标变到第一行,然后一次一次的覆盖打印,但是要记得刷新。
将进度条放在一个框框里面,并且显示进度,这个需要了解C语言的打印格式,C语言默认是右对齐,我们想要每次打印从左往右,就需要采取左对齐,再框框里面预留100个位置,因为一次是增加一个字符,我们来看代码:
等待旋转标志
这个是告诉用户进度条再运行,给个提示,使用字符 **|/-\**循环打印来模拟实现再旋转的场景,看代码:
我们的第一代进度条就算是完成了,我们再来完善一下,使他变得可控一些
代码:
1 #include"processbar.h"
2 void processbar()
3 {
4 char bar[NUM]={0};
5 int cnt=0;
6 const char*label="|/-\\";
7 int len=strlen(label);
8 while(cnt<=100)
9 {
10 printf("[%-100s][%d%%][%c]\r",bar,cnt,label[cnt%len]);
11 fflush(stdout);
12 usleep(50000);//控制进度条的时间
13 bar[cnt++]=STYLE;
14 }
15 printf("\n");
16 }
我们要是想完成这个进阶进度条,就不能把循环放进一个函数里面,这样是不可控的,我们要专门写一个下载函数,来进行软件的下载,把下载的进度单独设置成一个函数,什么叫下载的进度,下面这就叫下载的进度
1 #include"processbar.h"
2
3 //下载函数
4 void download(int tatol,int internetspeep)//传软件总大小和下载速度
5 {
6 initbar();
7 int cur=0;//从0MB开始下载
8 while(cur<=tatol)
9 {
10 processbar(cur*100/tatol,tatol,internetspeep);//乘100是因为传进去里面有百分比,传当前进度,总大小和下载速度
11 cur+=internetspeep;//internetspeepMB/s的速度下载
12 usleep(1000000);
13 }
14 printf("\n");
15 }
16 //进度函数
17 char bar[NUM]={0};//设置成全局的,因为再下载函数里面要频繁调用进度函数,而进度是一直推进的,放在进度函数里面会导致调用依次之后就会被销毁
18 void initbar()//每次任务下载的之前都要初始化一下
19 {
20 memset(bar,'\0',sizeof(bar));
21 }
22 void processbar(int rate,int tatol,int internetspeep)//传当前进度进来,就是已经下载的大小占总大小的多少
23 {
24 const char*label="|/-\\";
25 int len=strlen(label);
26 printf("[%-100s][%d%%][%c][%s%d%s]\r",bar,rate,label[rate%len],"已经下载了",tatol*rate/100,"MB");
27 fflush(stdout);
28 int count=internetspeep*100/tatol;
29 while(count--)//每次进度条增加几条字符,按照下载速度占总大小的百分比老算
30 {
31 bar[rate++]=STYLE;
32 }
33 }
这样已经很符合我们平时下载的进度条了,但是我还想再优化一下时进度条好看一些---------->这样的形式
代码优化:
颜色优化:(https://blog.csdn.net/wuquan_1230/article/details/106077560)参考这篇博客
代码:
今天大家实现了Linux上的第一个小程序,进度条,希望大家下来可以自己去实现一下,还是比较好玩的,也可以修改成自己风格的进度条,我们今天的知识就讲解到这里了,下篇再见