目录
\r 、\n、fflush
首先我们先来认识这三个东西,这将会是我们接下来两个小程序的重点之一
首先是我们的老演员\n,也就是回车加换行
这里面其实包含了两个操作,一个叫做回车,一个叫做换行
而单纯的回车就是指,我们现在的光标,回到当前行的最前面
而换行 + 回车就是来到下一行的最前面
而我们的 \r 就是单纯的回车,回到当前行的最前面
但是我们还有一个缓冲区的概念
也就是,当我们使用了\n之后就会自动刷新缓冲区,但是\r不会
我们不想用\n是因为不想出现下面这样的情况:
所以我们只想回到第一行然后从头开始覆盖式地进行写入
但是如果是\r的话,就会出现一个情况就是,我们会等到程序结束之后才会把最后一次的结果打印出来,这时因为缓冲区不会因为\r刷新
所以我们这时候就需要fflush(stdout),这样就能在不回车的前提下达到提前刷新缓冲区的效果了
至于stdout,这就是系统默认打开的标准输出文件,其实我们这里可以粗浅的看作就是显示器,我们需要刷新缓冲区才能让显示器上显示结果
倒计时
至于倒计时,其实相当简单,我们只需要知道,这是在不停地打印数字,并且会在同一个位置打印,这就需要用到回车(\r)回到最前面,然后覆盖掉前面的结果以达到原地变化的效果
但是由于\r不会刷新缓冲区,所以我们就需要使用fflush刷新,这样,我们的倒计时就写好了
代码如下:
void time_count_down(int total)
{
int cnt = total;
while(cnt >= 0)
{
printf("倒计时: %2d\r", cnt);
fflush(stdout);
cnt--;
usleep(300000);
}
printf("\n");
}
进度条
首先,我们需要知道的是,进度条的本质和倒计时是一样的,就是\r移动光标,通过覆盖式写入达到变化的效果
我们先来看看成品的样子:
分析一下,首先我们需要预留出一百个空间,表现出递进的效果
这一步我们可以直接建一个数组,然后一个循环,每循环一次,就打印出当前数组里面的内容,并且在当前数组最后一个 “ = ” 后面再加一个等号,这样,我们就达到了循环递进的效果
但是,我们需要注意的是,这期间我们需要一直使用 \r 和 fflush,如下:
#define LENGTH 101
#define STYLE '='
void ProcessBar()
{
char bar[LENGTH];
memset(bar, '\0', sizeof bar);
int cnt = 0;
while(cnt <= 100)
{
printf("[%-100s]\r", bar);
fflush(stdout);
bar[cnt++] = STYLE;
usleep(10000);
}
printf("\n");
}
注意,上面的 “%-100d” 中,100就代表默认留出100个位置,-100则代表左对齐
接着,我们可以看到,进度条后面还有两个东西:
这两个,一个是实时显示当前加载进度的数字显示,还有一个是想通过 |/-\ 这四个字符,达成一条线在不停旋转的动态效果,也就是在提醒用户,可能用户看到进度条不动了以为是网卡了,但其实还在下载,只是进度比较慢,暂时卡住了而已,动态的话就是为了避免这个问题
首先来解决第一个
这个其实我们只需要把数字填进去就可以了,只是后面的%需要写两个而已,写一个显示不出来
而最后一个就是,我们先写一个数组:
const char* label = "|/-\\";
我们只需要不停的从左往右选择即可,但是由于会越界,所以我们就需要%一个数组大小,这样才不会越界
而其中 \\ 有两个是因为只写一个不显示
比如,我现在数组大小是4,如果当前数字大小是4,4%4 = 0,那么就会直接回到第一个数组下标位置,5%4就是1,代表第二个位置,依此类推
总代码如下:
#define LENGTH 101
#define STYLE '='
const char* label = "|/-\\";
void ProcessBar()
{
char bar[LENGTH];
memset(bar, '\0', sizeof bar);
int len = strlen(label);
int cnt = 0;
while(cnt <= 100)
{
printf("[%-100s][%3d%%][%c]\r", bar, cnt, label[cnt % len]);
fflush(stdout);
bar[cnt++] = STYLE;
usleep(10000);
}
printf("\n");
}
进度条进阶版
现实中,我们的进度条都是搭配了任务一起的
比如我们可以写一个下载的任务,影响下载的因素这里假设只有带宽,他就相当于下载速度吧在这里
然后我们只需要提供一个文件总大小,模拟这个过程即可
代码如下(模拟下载任务的代码):
#define bandwidth 1024*1024*1.0;
void download(double file_size)
{
double current = 0.0;
while(current <= file_size)
{
//调用进度条
ProcessBar(file_size, current);
current += bandwidth;
usleep(20000);
}
printf("\n");
}
这时候我们的进度条文件就需要更改一下了
由于会被频繁调用,所以我们的进度条在这里,每一次调用都代表那一瞬间的状态
所以我们在进度条那里的循环可以只是循环添加 =
接着,我们的进度这次就是按照百分比来算的了
所以进度条需要一个总的文件大小,以及现在文件加载到哪里了,这样我们将两个文件除出来之后再乘100,就是当前文件加载的百分比进度:
double rate = current*100.0/total;
接下来的都是小小修改一下而已,代码如下:
#define LENGTH 101
#define STYLE '='
const char* label = "|/-\\";
void ProcessBar(double total, double current)
{
char bar[LENGTH];
memset(bar, '\0', sizeof bar);
int len = strlen(label);
double rate = current*100.0/total;
int loop_count = (int)rate;
int cnt = 0;
while(cnt <= loop_count)
{
bar[cnt++] = STYLE;
}
printf("[%-100s][%.1lf%%][%c]\r", bar, rate, label[cnt % len]);
fflush(stdout);
}
最后,我们还可以再来一个小优化
试想一下,我们以后如果有了图形化界面,或者就是单纯更好的进度条,对于这个下载任务而言,如果我们只是用一个函数指针的话,到时候我们只需要修改指针即可,这样的话,修改起来就很方便,当我们有了更好的进度条的时候
如下,我们可以先在 .h 文件里面定义一个函数指针:
typedef void (*pb)(double, double);
接着,我们的下载任务里面,就可以直接加上这个参数了:
#define bandwidth 1024*1024*1.0;
void download(double file_size, pb procbar)
{
double current = 0.0;
while(current <= file_size)
{
procbar(file_size, current);
current += bandwidth;
usleep(20000);
}
printf("\n");
}
int main()
{
download(100.0*1024*1024, ProcessBar);
return 0;
}
结语
这篇文章到这里就结束啦!!~( ̄▽ ̄)~*
如果觉得对你有帮助的,可以多多关注一下喔