在Linux下,我们安装软件时会经常看到进度条,来告知我们安装的进度。我们不妨自己模拟实现一个进度条,看看其中的细节。模拟实现进度条并不困难,但其中的细节我们又不可忽视。本篇文章会对模拟实现进度条进行详解。
一、进度条整体模板
谈到进度条,我们首先想到的是一个动态表示进度的显示程序。 首先,我们先来看一下进度条整体的模板,也就是我们所要模拟实现的进度条。如下:
模板1:
模板2:
我们看到,上述有两个模板,两个模板大同小异,只是输出格式不同而已。我们似乎已经有了一个大概的整体思路。无非就是格式打印输出嘛。真的是这样吗?确实是这样。但并不只是单纯的输入和输入。其中还涉及到了缓冲区和回车、换行的概念和细节。我们接着往下看。
二、输出缓冲区详解
我们平常使用的printf函数,是输出到屏幕。但是是直接输出到屏幕的吗?我们看下面的例子:
#include<stdio.h> int main() { int i=0; for(i=5;i>=0;i--) { printf("%d",i); sleep(1); //睡眠1秒 } return 0; }
我们看上述代码,输出结果是什么呢?我们第一反应是每间隔一秒依次输出5、4、3、2、1、0。我们直接看结果:
我们看到并非每间隔一秒依次输出5、4、3、2、1、0。而是过了6秒后直接输出了5、4、3、2、1、0。这是为什么呢?从这例子,我们间接了解到输出并非直接输出到屏幕上。那中间还有什么呢?其实,我们输出是先输出到了缓冲区。缓冲区又分为输入缓冲区和输出缓冲区。这里明显的是先输出到了输出缓冲区。
我们再看一个例子:
#include<stdio.h> int main() { int i=0; for(i=5;i>=0;i--) { printf("%d\n",i); sleep(1); //睡眠1秒 } return 0; }
上面的代码相对于第一个例子,我们多了一个换行。我们再看一下输出结果:
我们看到是每间隔一秒依次输出5、4、3、2、1、0。这又是为什么呢?这就关系到了把输出缓冲区的内容刷新到屏幕上的规则:
程序正常结束,作为main函数return操作的一部分,缓冲区被刷新。
当缓冲区的内容满了的时候,缓冲区会自动刷新到屏幕上。
可以使用操作符endl、fflush和 ends 来显示的刷新缓冲区。这三个都是IO库中的操作符,endl能完成换行和刷新缓冲区的工作。flush只完成刷新缓冲区的工作。而ends会向缓冲区插入一个空字符,然后刷新缓冲区。
了解到了输出缓冲区的刷新规则后,我们对上面的两个例子的输出结果就很容易理解了。当然,我们讲解了这么多输出缓冲区是为什呢?我们先回到模拟实现进度条,我们打印的进度条,是在一行上打印出的,并没有换行。这里就引出了两个问题:怎么做到不换行还能不断动态更新打印数据呢?怎么及时打印出缓冲区的数据呢?我们接着往下看。
三、模拟实现进度条思路及代码详解
3、1 回车和换行区别
我们上面的问题是:怎么做到不换行还能不断动态更新打印数据呢?首先换行( \ n )是不可以的。这里我们就可以用回车( \ r )。啊???换行和回车不一样吗?答案是不一样的。
回车 \r 本义是光标重新回到本行开头,r的英文return,控制字符可以写成CR,即Carriage Return。
换行 \n 本义是光标往下一行(不一定到下一行行首,还有可能是接上一行 行末另起一行。),n的英文newline,控制字符可以写成LF,即Line Feed。
但是回车并不能刷新缓冲区。所以我们还要用到fflush(stdout)来刷新缓冲区到屏幕上。我们可结合下面例子一起理解一下:
#include<stdio.h> #include<unistd.h> int main() { int i=0; for(i=5;i>=0;i--) { printf("%d\r",i); fflush(stdout); sleep(1); } return 0; }
通过不断的在同一行输出,我们可以得到一个类似倒计时的小程序,具体输出结果如下:
通过上面的例子,我们可以很好的理解回车和换行的区别。同时又解决了上述的两个问题。
3、2 模拟实现进度条思路详解
3、2、1 初阶进度条
首先,我们想到的是进度条完成是达到百分之百。那我们可以定义一个长度为105字符数组,这里多5个位置,是为了防止越界造成不必要的麻烦(注意,打印字符串时遇到‘\0’停止)。我们可通过100次循环,不断改变字符数组内容,再同过回车(\r)和格式化输出,我们就可以得到一个初步形式的进度条。我们结合代码一起理解:
#include<stdio.h> #include<unistd.h> #include<string.h> #define MAX 105 int main() { int i=0; char bar[MAX]; memset(bar,0,sizeof(bar)); //初始化字符数组为0 while(i<=100) { printf("[%-100s][%3d%%] \r",bar,i,); fflush(stdout); bar[i]='#'; i++; usleep(50000); } printf("\n"); return 0; }
上面我们可以看到有usleep函数。sleep函数睡眠时间是以秒为级别的。usleep是以微妙级别的。usleep(50000)也就是睡眠0.05秒。循环100次一共是睡眠5秒,也就是我们5秒多一点可以完成打印整个进度条。我们看输出结果:
3、2、2 进阶进度条
我们为了表示进度条在运行,我们可在最后加上一个旋转的字符表示正在运行。怎么加一个旋转的字符呢?我们可创建一个字符数组,存储旋转字符的不同状态。进而通过循环来控制字符旋转。我们可看如下代码:
#include<stdio.h> #include<unistd.h> #include<string.h> #define MAX 100 int main() { int i=0; char bar[MAX+5]; memset(bar,0,sizeof(bar)); const char* arr="|\\-/"; //旋转字符数组 while(i<=100) { printf("[%-100s][%3d%%] %c\r",bar,i,arr[i%4]); fflush(stdout); bar[i]='#'; //bar[i] = '-'; //bar[i+1] = '>'; //另一种格式 i++; usleep(50000); } printf("\n"); return 0; }
我们直接看输出结果:
最后会有一个动态表示的字符。这样看起来就会比较完整。当然,输出的格式我们可以自己选择。输出的颜色也可通过查询添加到自己的输入格式当中。
以上就是整个用C语言实现的进度条。其中有很多细节还是比较重要的,需要我们重点掌握。希望本篇文章会对你有所帮助,感谢观看ovo~