前言:经过这么多天的学习,想必大家学到了很多Linux知识,今天我们来用Linux来实现我们的第一个小程序 — — 进度条
在实现进度条之前,我们先了解一些之前没提到过的知识,以便我们理解
进度条
- 1. 缓冲区的概念
- 2. /r&&/n
- 3. 进度条
- 3.1 版本一
- 3.2 版本二
- 3.3 版本三
- 4. 总结拓展
1. 缓冲区的概念
我们先来分析下面几段代码感受一下行缓冲区的存在:
在Linux当中以下代码的运行结果是什么样的?
#include <stdio.h>#include <unistd.h> int main(){ printf("你能看见我嘛/n"); sleep(2); return 0;}
这段代码运行结果显而易见:
printf
函数直接打印内容,然后休眠2秒。
对于大家可能没有什么难度。那如果我们修改一下代码。
#include <stdio.h>#include <unistd.h> int main(){ printf("你能看见我嘛"); sleep(2); return 0;}
缓冲区的概念
通过视频我们发现,我仅仅是将/n
删除了,但是却带来了完全不一样的运行结果:先休眠2秒,然后才是printf
函数打印内容
那么为什么会出现这种情况呢? C语言执行代码的逻辑不应该是从上至下执行吗?
- 按照 C语言执行代码的逻辑
printf
确实已经运行了,只不过内容没有被显示出来!- 内容所在的区域则是在输出缓冲区中!
C/C++语言,会针对标准输出,给我们提供默认的缓冲区
fflush
函数可以刷新缓冲区,如果我们想立马显现可以用函数刷新fflush()
,而/n
是一种刷新的策略——行刷新,所以/n
也能立马显现!
2. /r&&/n
概念:
- /r: 回车,使光标回到本行首格
- /n: 换行,使光标移到下一行
光说可能大家不太理解,我们来实操看看:
#include <stdio.h>#include <unistd.h>int main(){ int cnt = 10; while(cnt) { printf("%d/r", cnt--); fflush(stdout); sleep(1); } return 0;}
回车 /r
我们可以看到在输出下一个数之前都让光标先回到本行首格。
但是为什么输出结果和我预想的完全不一样?
printf("%-2d/r", cnt--);
我们以两位字符进行输出,-则是表示靠左对齐,就可以正常输出了!
3. 进度条
在进行上面的铺垫之后,我们开始编写我们的第一个小程序。我们将用两个源文件和一个头文件,一个申明,一个调用,一个实现
test.c:实现
test.h:申明
main.c:调用
// Makefile:mytest:test.c main.c gcc -o $@ $^.PHONY:cleanclean: rm -rf mytest
3.1 版本一
在版本一中,我们只要简单实现一下基本的功能,得到一个基本框架就足够了。
// process_v1//test.h:申明pragma once#include <stdio.h>#include <string.h>#include <unistd.h> // 定义进度条的总长度,因为有'/0'的存在所以设为101#define SIZE 101 // 定义进度条的当前进度#define MAX_RATE 100// 进度条的符号#define STYLE '#'// 进度条的休眠时间#define STIME 1000*15void process_v1();.............//test.c:实现#include "test.h" // 旋转光标const char *str = "|/-//";// //:才能表示一个'/'void process_v1(){ int rate = 0; //初始化进度条全为'/0' char bar[SIZE] = {0}; //循环打印 int num = strlen(str); while(rate <= MAX_RATE) { // 进度条的打印格式 // -100:先取好[]的范围,然后靠左打印。 printf("[%-100s][%d%%][%c]/r", bar, rate, str[rate%num]); // 刷新缓冲区 fflush(stdout); // 休眠时长 usleep(STIME); // 填充符号 bar[rate++] = STYLE; } // 刷新 printf("/n"); }.............//main.c:调用#include "test.h>int main(){ process(); return 0;}
进度条:版本一
我们的第一代进度条也就完成了,实现了基本的结构框架!而我们的进度条,肯定不能干自己的,一定是和某种任务关联起来的!
3.2 版本二
我们将循环改成内部维护一个简单的静态缓冲区,每次往缓冲区里面增加内容然后刷新缓冲区内容就可以
不能一次将进度条打印完毕,否则不能与场景更好的结合
// process_v2//test.h:申明#pragma once #include <stdio.h>#include <string.h>#include <unistd.h> #define SIZE 101#define MAX_RATE 100#define STYLE '#'#define STIME 1000*15 typedef void(*callback_t)(int); // 回调函数 // 这里我们也可以使用函数指针 void process_v2(int); ~ .............//main.c:调用#include "test.h" #define TARGET_SIZE 1024*1024 // 模拟下载软件的大小#define DSIZE 1024*10 // 模拟下载的速度 //void download()//{// int target = TARGET_SIZE; // 软件总体积// int total = 0; // 当前下载的大小// // while(total < target)// {// usleep(STIME); // 用休眠时间,模拟下载时间// total += DSIZE;// process_v2(total*100/target); // }// printf("/n");//}// 回调函数void download(callback_t cb){ int target = TARGET_SIZE; // 软件总体积 int total = 0; // 当前下载的大小 while(total < target) { usleep(STIME); // 用休眠时间,模拟下载时间 total += DSIZE; int rate = total*100/target; cb(rate); } printf("/n"); } int main(){ download(process_v2); return 0;}.............//test.c:实现#include "test.h" const char *str = "|/-//";void process_v2(int rate){ static char bar[SIZE] = {0}; int num = strlen(str); if(rate <= MAX_RATE && rate >= 0) { printf("[%-100s][%d%%][%c]/r", bar, rate, str[rate%num]); fflush(stdout); bar[rate] = STYLE; } if(rate == MAX_RATE) { // 重新刷新为0 memset(bar, '/0', sizeof(bar)); }}
进度条:版本二
我们也能完成进度条的实现,最后我们在优化一下,变成我们的版本三!。
3.3 版本三
因为版本二已经能将进度条完美的呈现了,我们版本三,只是在二的基础上,美化一下,所以只是简单修改一点代码!
// test.h#define STYLE_DEADER '>'#define STYLE_BODY '='typedef void (*callback_t)(double);.............// main.c// 我们在下载时,模拟下载中断的状态void download(callback_t cb){ int target = TARGET_SIZE; // 软件总体积 int total = 0; // 当前下载的大小 while(total <= target) { usleep(STIME); // 用休眠时间,模拟下载时间 total += DSIZE; double rate = total*100/target; // 我们让下载进度永远维持在50%左右 if(rate > 50.0) { total = target/2; } cb(rate); } printf("/n"); } .............// test.c if(rate <= MAX_RATE && rate >= 0) { // 设置cnt是为了在下载终止时,光标依然能变化 cnt++; cnt = cnt > num ? 0 : cnt; // /033[1;47;30m ... /033[0m 则是更改输出时字体和背景颜色 printf("加载中.../033[1;47;30m%-100s/033[0m][%.1lf%%][%c]/r", bar, rate, str[cnt]); fflush(stdout); if(rate < MAX_RATE) { bar[(int)rate] = STYLE_BODY; bar[(int)rate+1] = STYLE_HEADER; } else{ bar[(int)rate] = STYLE_BODY; } }
进度条:版本三
我们可以发现,我们修改了字体颜色和背景,设置测试了可能遇到的中断情况,光标依旧会变化。当然了进度条还有很多情景,等待着各位开发!
4. 总结拓展
拓展:
关于print带颜格式化输出,我在这里推荐一篇博客,有兴趣的可以去了解一下
print带颜格式化输出
总结:
本篇我们简单了解了一下缓冲区,以及换行'/n'
与回车'/r'
的基本概念,然后由浅入深的介绍了三个版本的进度条,当然了美化方式各位都不一样,都是可以的,我们的Linux第一个小程序就讲到这里
谢谢大家支持本篇到这里就结束了