文章目录
🎉博客主页:小智_x0___0x_
🎉欢迎关注:👍点赞🙌收藏✍️留言
🎉系列专栏:Linux入门到精通
🎉代码仓库:小智的代码仓库
一、预备知识
1.1回车换行
我们一般意义上的回车换行是两个概念,一般我们在C语言上的
\n
的作用是回车+换行,他把两个操作都做了,但是
\转义字符
也有别的的作用,我们一般回车是回车,换行是换行,两个是不同的概念。回车是指讲光标移动到当前行的最左侧开始位置,换行指的是光标水平方向保持不变,向下平移一行,C语言中有一个
\r
就是实现回车功能。
我们平时用的键盘上面有一个
Enter
的按键,这个按键就是实现了我们的回车+换行的作用。
1.2缓冲区
我们先来看一段代码:
#include"processBar.h"#include<unistd.h>intmain(){printf("hello world\n");sleep(2);return0;}
这个程序是输出一个
hello world
然后再让程序休眠2秒,这里的
sleep
函数是系统函数功能是让程序休眠指定时间,我们可以通过
man 3 sleep
手册来查看
sleep
函数的使用方法。
我们来运行这个代码来看看效果:
这里可以看到的现象是,程序先完成了打印
hello world
再休眠两秒钟。
我们再来改改代码:
#include"processBar.h"#include<unistd.h>intmain(){printf("hello world");sleep(2);return0;}
这次我们把
\n
去掉,此时程序会是怎么样的呢,先打印还是先休眠,我们一起来验证一下:
可以看到我们把
\n
去掉之后程序是先休眠了两秒,接着再在显示器上打印出
hello world
,这次没有换行符,所以
bash
命令行就会紧接在
hello world
的后面。
那么事实真的是这样的嘛?
我们可以来想一想,一个C语言程序是严格按照代码顺序从上往下依次执行的,不管怎样都是这样,肯定是先执行打印
printf
在进行
sleep
休眠2秒,但是此时又有一个疑问了,为什么我们
printf
的内容没有显示出来呢?在我
sleep
期间
hello world
在哪里?
一定被保存起来了,那么要保存就一定需要内存空间,这里其实就是保存在来我们的缓冲区里面,这里的缓冲区是由C语言维护的一段内存
所以当程序执行结束的时候才会将缓冲区中的内容刷新出来。
那我们要强制刷新呢?
这里我们就要知道一个C语言程序运行会默认帮我们打开这三个流:
- stdin - - - -标准输入流(键盘)
- stdout - - - - 标准输出流(显示器)
- stderr - - - -标准错误流(显示器)
在我们平时使用文件操作的时候打开一个文件也是用的
FILE *
类型来打开文件。
那么我们要强制刷新就要用到这个函数
fflush
这里刚好就是接收
FILE*
类型。
具体操作:
#include"processBar.h"#include<unistd.h>intmain(){printf("hello world");fflush(stdout);sleep(2);return0;}
我们其实打印出的数据是往
stdout
中打印的,所以我们在
printf
后面紧接着一个
fflush
来强制刷新。
我们来运行看看现象:
可以看到这次我们是先显示出了
hello warld
再休眠两秒,
bash
的命令行才刷新出来。
总结:
\n
可以刷新缓冲区fflush
可以强制刷新缓冲区- 程序结束可以刷新缓冲区
二、倒计时
有了上面的基础我们可以简单的来实现一个倒计时:
#include"processBar.h"#include<unistd.h>intmain(){int cnt=10;//定义倒计时时间while(cnt>=0){printf("%-2d\r",cnt);//%-2d 表示以两个字符位输出并以左对齐方式输出 `\r`表示只回车不换行fflush(stdout);//强制刷新缓冲区
cnt--;sleep(1);//程序休眠1s}printf("\n");return0;}
我们再来看看效果:
在这段代码中,
%-2d\r
的作用是实现倒计时效果。
%-2d
表示以两个字符位输出并以左对齐方式输出,
%d
是输出整数的占位符。
\r
表示回车到行首,即光标移到行首,而不换行。
因此,每次循环时,数字会被输出并覆盖上一次输出的数字,从而实现倒计时的效果。由于使用了
\r
回车到行首,所以数字输出在同一行上,不会换行。
另外,
fflush(stdout)
强制刷新缓冲区是为了确保每次输出都能够立即显示在屏幕上,而不是留在缓冲区中等待下一次输出。
sleep(1)
函数是让程序休眠1秒钟,以便实现倒计时效果。
三、进度条
设计思路:
- 确定进度条的显示方式,例如使用字符 ‘#’ 表示进度条的进度。
- 确定进度条的长度,例如设定进度条长度为100个字符。
- 计算进度条的进度,例如已完成任务的百分比为 50%,则进度条应该显示50个 ‘#’ 字符。
- 每次更新进度条时,先将光标移动到行首,然后输出当前进度条的状态,再将光标移回到行首,以便下一次更新。
- 可以使用定时器或者其他方式控制进度条的更新速度,例如每隔1秒更新一次进度条。
3.1普通版本源代码
processBar.h
#pragma once #include<stdio.h>#define NUM 102 // 进度条长度#define TOP 100 // 进度条最大值#define BODY '=' // 进度条已完成部分的字符#define RIGHT '>' // 进度条右边界的字符// 进度条函数的声明externvoidprocessbar(int speed);
processBar.c
#include"processBar.h"#include<string.h>#include<unistd.h>// 进度条的四种状态,即 |、/、-、\constchar*lable="|/-\\";voidprocessbar(int speed){char bar[NUM];// 存储进度条的字符数组memset(bar,'\0',sizeof(bar));// 初始化进度条数组int len =strlen(lable);// 计算进度条状态的长度int cnt =0;// 进度条的当前值while(cnt <= TOP)// 当进度条的当前值小于等于最大值时,继续循环{printf("[%-100s][%d%%][%c]\r",bar,cnt,lable[cnt%len]);// 输出进度条信息fflush(stdout);// 刷新缓冲区,使得程序能够立即输出
bar[cnt++]= BODY;// 将 BODY 字符加入到进度条数组中,并将当前值加1if(cnt<100) bar[cnt]= RIGHT;// 当进度条未达到100%时,在进度条的末尾加上 RIGHT 字符,防止有边界越界usleep(speed);// 程序休眠一段时间,以控制进度条的更新速度}printf("\n");// 输出提示信息,任务已完成}
main.c
#include"processBar.h"#include<unistd.h>intmain(){processbar(100000);return0;}
代码效果:
3.2高级版本源代码
在现实中进度条是表示我们下载某些文件的进度,所以进度不是由我们自己来决定的需要,所以我们需要写一个进度条接口来接收当前下载的百分比,进而通过调用函数来打印出当前的进度。
plus版本:
processBar.h
#pragma once #include<stdio.h>#define NUM 102 // 进度条长度#define TOP 100 // 进度条最大值#define BODY '=' // 进度条已完成部分的字符#define RIGHT '>' // 进度条右边界的字符externvoidprocessbar(int rate);
processBar.c
#include"processBar.h"#include<string.h>#include<unistd.h>// 进度条的四种状态,即 |、/、-、\constchar*lable="|/-\\";// 存储进度条的字符数组,初始化为0char bar[NUM]={0};voidprocessbar(int rate){if(rate <0|| rate >100)return;// 判断进度条的值是否在合法范围内int len =strlen(lable);// 计算进度条状态的长度printf("[%-100s][%d%%][%c]\r",bar,rate,lable[rate%len]);// 输出进度条信息fflush(stdout);// 刷新缓冲区,使得程序能够立即输出
bar[rate++]= BODY;// 将 BODY 字符加入到进度条数组中,并将当前值加1if(rate<100) bar[rate]= RIGHT;// 当进度条未达到100%时,在进度条的末尾加上 RIGHT 字符,防止有边界越界添加}
main.c
#include"processBar.h"#include<unistd.h>intmain(){int total =1000;//要下载的总进度int curr =0;//初始进度while(curr <= total){processbar(curr*100/total);
curr+=10;//每次下载10usleep(50000);//模拟下载花费的时间}printf("\n");return0;}
plusplus版本:
processBar.h
#pragma once #include<stdio.h>#define NUM 102 // 进度条长度#define TOP 100 // 进度条最大值#define BODY '=' // 进度条已完成部分的字符#define RIGHT '>' // 进度条右边界的字符// 进度条函数的声明externvoidprocessbar(int rate);externvoidinitbar();
processBar.c
#include"processBar.h"#include<string.h>#include<unistd.h>// 定义了一些控制台输出颜色的宏#define NONE "\033[m"#define RED "\033[0;32;31M"#define GREEN "\033[0;32;32m"#define LIGHT_BLUE "\033[1;34m"#define LIGHT_PURPLE "\033[1;35m"// 进度条的四种状态,即 |、/、-、\constchar*lable="|/-\\";// 存储进度条的字符数组,初始化为0char bar[NUM]={0};// 进度条函数的具体实现部分,实现了进度条的显示、刷新、更新等功能voidprocessbar(int rate){if(rate <0|| rate >100)return;// 判断进度条的值是否在合法范围内if(rate==0)initbar();// 如果进度条为0,则初始化进度条数组int len =strlen(lable);// 计算进度条状态的长度printf("["LIGHT_BLUE"%-100s"NONE"]""[%d%%][%c]\r",bar,rate,lable[rate%len]);// 输出进度条信息,带有颜色fflush(stdout);// 刷新缓冲区,使得程序能够立即输出
bar[rate++]= BODY;// 将 BODY 字符加入到进度条数组中,并将当前值加1if(rate<100) bar[rate]= RIGHT;// 当进度条未达到100%时,在进度条的末尾加上 RIGHT 字符,以便显示进度条的右边界}// 初始化进度条数组voidinitbar(){memset(bar,'\0',sizeof(bar));}
main.c
#include"processBar.h"#include<unistd.h>// 定义了一个函数指针类型 callback_ttypedefvoid(*callback_t)(int);// 模拟一种安装或者下载的任务voiddownLoad(callback_t cb){int total =1000;// 总大小为1000MBint curr =0;// 当前下载大小为0MBwhile(curr <= total){usleep(50000);// 模拟下载花费的时间int rate = curr*100/total;// 计算当前下载进度cb(rate);// 通过回调函数展示进度
curr +=10;// 循环下载了一部分}printf("\n");// 输出提示信息,任务已完成}intmain(){printf("donwnload 1: \n");downLoad(processbar);// 下载任务1,回调函数为 processbarprintf("donwnload 2: \n");downLoad(processbar);// 下载任务2,回调函数为 processbarprintf("donwnload 3: \n");downLoad(processbar);// 下载任务3,回调函数为 processbarprintf("donwnload 4: \n");downLoad(processbar);// 下载任务4,回调函数为 processbarreturn0;}
上面的代码实现了一个简单的下载任务,并通过回调函数
processbar
实现了下载进度的显示。代码主要分为以下几个部分:
- 头文件部分,包含了
stdio.h
头文件和processBar.h
头文件,以及一些宏定义。 - 进度条函数的声明部分,声明了进度条函数
processbar
和初始化进度条数组的函数initbar
。 - 进度条函数的具体实现部分,实现了进度条的显示、刷新、更新等功能。这部分代码和之前相同。
- 初始化进度条数组的函数
initbar
的具体实现部分。这个函数只是简单地将进度条数组清零。 - 主函数部分,模拟了四个下载任务,并通过回调函数
processbar
展示下载进度。具体来说,这部分代码主要做了以下几件事情:- 调用downLoad
函数模拟四个下载任务,并将回调函数设置为processbar
。- 在每个下载任务开始时输出提示信息。- 在每个下载任务结束时输出提示信息。
代码运行效果:
🍀小结🍀
今天我们学习了
"Linux进度条小程序"
相信大家看完有一定的收获。**
种一棵树的最好时间是十年前,其次是现在!
** 把握好当下,合理利用时间努力奋斗,相信大家一定会实现自己的目标!加油!创作不易,辛苦各位小伙伴们动动小手,
三连一波💕💕~~~
,本文中也有不足之处,欢迎各位随时私信点评指正!
版权归原作者 .小智 所有, 如有侵权,请联系我们删除。