【Linux】倒计时和进度条实现

目录

前言

【Linux】自动化构建--make/Makefile详情请点击,今天继续介绍【Linux】倒计时小程序和进度条程序

一、知识铺垫

1. 回车换行

回车代表的意思是让光标回到当前行的起始位置,换行是换到下一行的意思

  • 我们使用"\r"表示回车,"\n"表示换行

2. 缓冲区

  1. 我们在test.c中写入下面代码,sleep(2):休眠2秒,sleep头文件是#include< unistd.h >
  2. 使用gcc编译链接再运行,发现开始的时候没有显示"hello world",等待2s后再显示屏上打印"hello world",按照我们正常C语言逻辑我们可以知道,程序是从上到下一条条执行的,按照正常逻辑应该先执行"hello world",在sleep 2s,然后程序结束,为什么会是我们看到的那样呢?
  3. 程序后面能打印出"hello world"说明字符串并没有丢失,而是先被保存起来了,这个保存的位置叫做缓冲区,当程序遇到"\n"或者程序结束时会将缓冲区的内容在显示屏显示出来,由于printf函数后面没有"\n",所以该语句暂时保存在缓冲区,sleep 2s后程序结束输出到显示屏上
  4. 当在"hello world"语句后面加入"\n"后,先在显示屏输出"hello world",再sleep 2s,程序结束
  5. 如果在不加"\n"的情况下,如何让程序立刻输出"hello world",再sleep 2s,程序结束?
  • 可以使用fflush函数强制刷新缓冲区内容,fflush需要传对应的流,流分为:标准输入流(stdin)、标准输出流(stdout)、标准错误流
  • "hello world"是要输出到显示屏上,因此传入标准输出流,使用fflush后先输出"hello world",再sleep 2s后程序结束,且没有换行

二、倒计时小程序

  1. 我们默认生成10s倒计时
  2. 使用while循环来打印的倒计时数字并设置sleep,这样才能看到倒计时效果,注意这里需要使用fflush函数强制刷新缓冲区内容到显示屏上
  3. 数字是以字符显示到显示屏上的,数字10在显示屏打印出来时是字符1和字符0打印到显示屏上,当倒计时数字是9时,是一个字符,那么0那个字符并不会被覆盖,因此我们要使用%-2d让数字打印占两个字符,且左对齐打印
  4. 最后倒计时完毕后加入换行符,防止倒计时的0被命令行覆盖
cpp 复制代码
 #include<stdio.h>
 #include<unistd.h>

 int main()
 {
     int cnt = 10;
     while(cnt >= 0)
     {
         printf("%-2d\r", cnt);
         fflush(stdout);
         sleep(1);                                                                                             
         cnt--;
     }
     printf("\n");
     return 0;
 }

三、进度条小程序

进度条样例

1. 进度条理论代码

  1. 我们创建processBar.c文件编写进度条代码的实现,main.c文件编写进度条函数和基本的框架,processBar.h声明进度条函数。同时使用make/makefile自动化构建
  2. processBar.h中,为了防止头文件被重复包含,使用#pragma once,同时声明进度条函数void Process()
  3. 包含头文件#include<string.h>和#include<unistd.h>,在初始化processbuff时使用memset需要string头文件,使用usleep时需要unistd头文件
cpp 复制代码
#pragma once
#include <stdio.h>
#include<string.h>
#include<unistd.h>   
                                                                                         
void Process();
  1. processBar.c中,包含#include<processBar.h>,宏定义数组大小和加载符号
  2. 根据上面进度条实例,我们需要打印processbuff、进度百分比和加载符,同时要使用fflush函数强制缓冲区刷新,实时将进度更新到显示屏上,再useep(50000)
  3. 最后进度条更新到100%后换行,防止命令行覆盖进度条
cpp 复制代码
#include"processBar.h"

#define SIZE 101
#define STYLE '='
void Process()
{
     const char* label = "|/-\\";
     int len = strlen(label);
     char processbuff[101];
     memset(processbuff, '\0', SIZE);
     int cnt = 0;
     while(cnt <= 100)
     {
         printf("[%-100s][%d%%][%c]\r", processbuff, cnt, label[cnt%len]);
         fflush(stdout);
         processbuff[cnt++] = STYLE;
         usleep(50000);
     }
     printf("\n");
 }     
  1. main.c中,也需要包含#include<processBar.h>,直接调用Process函数
cpp 复制代码
 #include"processBar.h"
 int main()
{
     Process();                                                                                                
     return 0;
}

2. 进度条实战版本

生活中,进度条往往运用在软件下载过程中,由于网速和软件包大小不同软件下载时间也是不同的,进度条速度也在实时变化

  1. processBar.h中,为了防止头文件被重复包含,使用#pragma once,同时声明进度条函数void FlashProcess(double total,double cur)
  2. 包含头文件#include<string.h>和#include<unistd.h>,在初始化processbuff时使用memset需要string头文件,使用usleep时需要unistd头文件,使用#include<time.h>和#include<stdlib.h>生成随机数来模拟下载网速波动
cpp 复制代码
#pragma once
#include <stdio.h>
#include<string.h>
#include<unistd.h>
#include<time.h>
#include<stdlib.h>  
  
void FlashProcess(double total,double cur); // 更新进度,按照实时下载进度更新进度条
  1. processBar.c中,包含#include<processBar.h>,宏定义数组大小和加载符号
  2. 对于进度条进度'='的更新问题,由于等号不可能出现0.13等小数个等号,所以取整
  3. 对于进度条的旋转光标的实现,在正常下如果下载较慢(进度条不更新,rate也没有更新),怎么判断是否还在下载,还是说只是下载较慢,暂时没有更新。通过旋转光标是否转动来判断,所以旋转光标的转换不能和total和cur挂钩,而是和调用FlashProcess函数的次数来更新,只要还在调用该函数说明还在下载中
  4. 再将进度条实时刷新到显示屏上,而不是等函数调用完成之后自动刷新到显示屏上,所以使用fflush(stdout);
  5. 当进度条到达100%之后,还需要换行,避免命令行覆盖进度条
cpp 复制代码
  1 #include"processBar.h"
  2 
  3 
  4 #define SIZE 101
  5 #define STYLE '='

  8 void FlashProcess(double total,double cur)
  9 {
 10     if(cur > total)
 11         cur = total;
 12     double rate = cur / total * 100; //转化为0.1->10%这样的形式
 13     int cnt = (int) rate;// 10.4->10
 14     char processbuff[SIZE];
 15     memset(processbuff, '\0', SIZE);
 16     int i = 0;
 17     for(; i < cnt; i++)
 18     {
 19         processbuff[i] = STYLE;
 20     }
 21     static const char* label = "|/-\\";
 22     static int index = 0;
 23     printf("[%-100s][%.lf%%][%c]\r",processbuff, rate, label[index++]);                                       
 24     index %= strlen(label);
 25     fflush(stdout);
 26     if(cur >= total)
 27         printf("\n");
 28 }
  1. main.c中,也需要包含#include<processBar.h>,需要一个下载函数,当cur > total时,将cur = total,再刷新到显示屏上跳出循环
  2. 模拟实现网速波动使用函数SpeedFloat,限制网速波动范围
cpp 复制代码
  1 #include"processBar.h"

  5 double SpeedFloat(double start, double range)
  6 {
  7     int int_range = (int)range;
  8     return start + rand() % int_range + (range - int_range);
  9 
 10 }

 11 void Download(double total)
 12 {
 13     srand(time(NULL));
 14     double cur = 0.0;
 15     while(1)
 16     {
 17         if(cur > total)
 18         {
 19             cur = total;
 20             FlashProcess(total, cur);
 21             break;
 22         }
 23         FlashProcess(total, cur); // 更新进度,按照实时下载进度更新进度条
 25         cur += SpeedFloat(speed, 20.3);
 26         usleep(30000);                                                                                        
 27     }
 28 }

 30 int main()
 31 {
 32     printf("Download:20.0MB\n");
 33     Download(20.0);
 34     printf("Download:200.0MB\n");
 35     Download(200.0);
 36     printf("Download:2000.0MB\n");
 37     Download(2000.0);
 38     printf("Download:20000.0MB\n");
 39     Download(20000.0);
 40     return 0;
 41 }
  1. 我们还可以定义函数指针在调用Download函数的时候直接调用进度条更新函数Download(double total, callback_t cb)
cpp 复制代码
  5 //函数指针类型  
  6 typedef void (* callback_t)(double, double); 
 14 //cb:回调函数                 
 15 void Download(double total, callback_t cb)
 16 {                              
 17     srand(time(NULL));         
 18     double cur = 0.0;                                                                                         
 19     while(1)                   
 20     {                          
 21         if(cur > total)        
 22         {                      
 23             cur = total;
 24             cb(total, cur);
 25             break;
 26         }
 27         cb(total, cur); // 更新进度,按照实时下载进度更新进度条
 28        // cur += speed; //模拟下载
 29         cur += SpeedFloat(speed, 20.3);
 30         usleep(30000);
 31     }
 32 }
 
 34 int main()
 35 {
 36     printf("Download:20.0MB\n");                                                                              
 37     Download(20.0, FlashProcess);
 38     printf("Download:200.0MB\n");
 39     Download(200.0, FlashProcess);
 40     printf("Download:2000.0MB\n");
 41     Download(2000.0, FlashProcess);
 42     printf("Download:20000.0MB\n");
 43     Download(20000.0, FlashProcess);
 44     return 0;
 45 }

运行结果如下:模拟实现了不同网速下的进度条下载过程

相关推荐
怀旧,9 小时前
【Linux系统编程】3. Linux基本指令(下)
linux·开发语言·c++
艾莉丝努力练剑9 小时前
【C++STL :stack && queue (三) 】优先级队列的使用以及底层实现
linux·开发语言·数据结构·c++·stl
web安全工具库10 小时前
Makefile 模式规则精讲:从 %.o: %.c 到静态模式规则的终极自动化
linux·运维·c语言·开发语言·数据库·自动化
江公望14 小时前
Qt的环境变量QT_QPA_PLATFORM浅解
linux·qt
Wang's Blog15 小时前
Linux小课堂: 文件操作核心命令深度解析(cat、less、head、tail、touch 与 mkdir 命令)
linux·chrome·less
Do_GH17 小时前
【Linux】07.Ubuntu开发环境部署
linux·运维·ubuntu
CHH321317 小时前
在 Mac/linux 的 VSCode 中使用Remote-SSH远程连接 Windows
linux·windows·vscode·macos
tryCbest17 小时前
Linux使用Docker部署项目后期更新
linux·运维·docker
孤独得猿18 小时前
聊天室项目开发——etcd的安装和使用
linux·服务器·c++·etcd
siriuuus18 小时前
Linux Tomcat 简单使用及 Nginx 反向代理
linux·nginx·tomcat