前言
在我们的日常生活中,进度条是十分常见的,比如在软件下载中,应用加载中等等~~~那么进度条有什么特点?他又如何实现。 下面我们将结合下面的图展开讲解
一、前置理论知识
1.1回车和换行的区别
在我们的日常生活中,回车和换行似乎是相同的事情,但是事实上
- 回车:光标回到当前行的左端
- 换行:跳转到当前的下一行
- 在C语言中"\n"表示回车加换行,"\r"单单指的是回车;
PS:现在我们键盘上的ENTER同时兼备了回车和换行的功能,
我们可以看到在老式的键盘中回车键的符号和对应的形状都是一个先向下再向左的箭头。也就是说这一个键同时实现了两个功能。
1.2缓存区的概念
在C语言中,缓存区是一种存储空间,用于存储程序中经常使用的变量和数据。缓存区的目的是提高程序的运行速度,当程序需要访问一个变量或数据时,处理器会先检查缓存区中是否存在该数据的副本,如果存在,则直接访问缓存区中的数据,而不是从内存中读取。这样可以减少内存访问次数,提高程序的执行速度。
在C语言中会默认打开三个输入输出的文件,分别是标准输入流stdin,标准输出流stdout,标准错误stderr。 在我们平时的程序中,从程序中输出的数据或者键盘上读取的数据会先存到一个缓存区中,在需要时经行读取和截断。(比如:在printf函数调用中输入带有空格的字符串,空格后的数据未被访问,存入缓存区)
1.2.1“\n”和“\r”在缓冲区中的作用效果差别
- 在Linux中我们经常说一句话:万物皆文件。也就是Linux系统认为显示器是文件,键盘是文件。在我们使用printf函数会将缓存区的数据读取到显示器上。
- 要注意的是显示器默认是行刷新,也就是当输出内容配上“\n”后会直接回车+换行并立刻将内容打印在屏幕上,待睡眠结束后,才会弹出操作框;
- 而当输出内容配上“\r”后,其会先输出到缓冲区中,待睡眠结束后才会和操作框一起输出;
代码语言:javascript
复制
printf("hello Makefile!\n");先行打印,待睡眠结束后才弹出操作框 | |
printf("hello Makefile!\r");待睡眠结束后才会和操作框一起打印 |
1.2.2fflush函数
- fflush是一个在C语言标准输入输出库中的函数,功能是冲洗流中的信息,该函数通常用于处理磁盘文件。fflush()会强迫将缓冲区内的数据写回参数stream 指定的文件中。
- 在下面代码块中,"hello Makefile!"本来应该待睡眠结束后才会和操作框一起弹出,在此之前存储在缓冲区中;但我们用了fflush(stdout);(stdout是其数据类型),直接将内容从缓冲区中写回显示器文件中;
- 最终完成的效果是:先行打印,待睡眠结束后才弹出操作框
代码语言:javascript
复制
int main() | |
{ | |
printf("hello Makefile!\r"); | |
fflush(stdout); | |
sleep(3); | |
return 0; | |
} |
二、Linux中的先行配置
在Linux中我们一般使用gcc命令去完成代码的预处理、编译、汇编、连接的工作。而对于一个多文件形成的程序我们通常使用Linux项目自动化构建工具-make/Makefifile。可以说会不会写makefile反映出一个人有无能力去完成大型工程的能力。
虽然进度条只由三个文件组成但是这不失为一种练习vim,makefile的手段。
- 用vim分别建立【main.c】【makefile】【processbar】【processBar.c】【processbar.h】,可视具体情况设置,文末有简略代码;
- makefile文件如图所示
> - ./processbar 执行程序
三、进度条的重点部分
1、设计进度条的主体部分
这里我们单独实现进度条功能,所以我们需要一个东西来模拟下载的速度从而让进度条以可以被观测的方式来实现。
sleep/usleep(time)让程序休眠time秒。每次休眠结束后更新进度条,在运行时就像进度条真的在加载什么。
将进度条显现在屏幕上我们一般会用printf函数,那么我们究竟需要展现什么?
以这个为例,我们需要一条代表下载进度的线,表示已经下载的百分比和一些动态的符号表示程序的进行。
进度线:一个大小为102char数组(预留两个位置放"\0") 百分比:一个int变量 动态符号:一个固定的字符串比如:| / - \,然后在
那么我们的初代主体代码为
代码语言:javascript
复制
void processbar(int speed) | |
{ | |
char bar[102]; | |
const char *lable="|/-\\"; | |
memset(bar, '\0', sizeof(bar)); | |
int len =strlen(lable); | |
int cnt=0; | |
while(cnt <= 100) | |
{ printf("[%-100s][%d%%][%c]\r", bar, cnt, lable[cnt%len]); | |
fflush(stdout); | |
bar[cnt++]=BODY; | |
if(cnt<100)bar[cnt]='>'; | |
usleep(speed); | |
} | |
printf("\n"); | |
} |
这里注意两点一是 ‘ / ’ 的表示和‘ % ’ 的表示。
但是现实情况大多是传入进度来显示进度条。
所以我们将代码经行些许修改。
四、完整代码
代码语言:javascript
复制
const char *lable="|/-\\"; | |
char bar[NUM]; | |
void initbar() | |
{ | |
memset(bar, '\0', sizeof(bar)); | |
} | |
void processbar(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; | |
if(rate<100)bar[rate]=RIGHT; | |
} |
代码语言:javascript
复制
extern void processbar(int speed); | |
extern void initbar(); |
代码语言:javascript
复制
typedef void(*callback_t)(int); | |
//模拟下载 | |
void downLoad(callback_t cb) | |
{ | |
initbar(); | |
int total = 100;//100mb | |
int curr = 0;//0mb | |
while(curr <= total) | |
{ | |
usleep(10000);//模拟下载时间 | |
int rate=curr*100/total; | |
cb(rate); | |
curr++; | |
} | |
printf("\n"); | |
} | |
int main() | |
{ | |
downLoad(processbar); | |
return 0; | |
} |