当前位置: 首页 > ops >正文

Linux第一个系统程序-进度条(14)

文章目录

  • 事先说明(8-0.00.00)
  • 第一个版本
    • 解决进度条的增长
    • 调整比率(下载进度的) + 旋转光标
    • 代码
  • 第二个版本
    • 1. 代码展示
    • 2. 问题
    • 3. 添加旋转光标
    • 4. 完整代码
      • 4.1 main.c
      • 4. 2 process.c
    • 5. 补充(解耦)

进度条动图效果展示(动图展示不了光标旋转)。用到的Makefile模板获取

请添加图片描述

事先说明(8-0.00.00)

  • 思路展示:进度条的增长用#数量的增加来体现,当#数量增长,末尾数字也在不断增长,用一个\的不断旋转,来告诉用户是否在下载
    在这里插入图片描述

  • 这里需要写进度条的两个版本,第一个版本:正常展示进度条的基本功能。第二个版本:能用于工程项目当中

  • 准备三个文件:main.c,process.c,process.h
    在这里插入图片描述

第一个版本

解决进度条的增长

1. 先展示大致框架

  • process.h:函数的声明,process.c:函数的实现,main.c:进行函数的调用。process.h中要修改成 #include<string.h>
    在这里插入图片描述

  • 再将之前写的Makefile拷贝到你这三个文件所在的目录,记得去更改可执行程序的名称(BIN)为 process
    在这里插入图片描述


[xiao@hcss-ecs-28ce dir_process]$ make
编译... main.c 成 main.o
编译... process.c 成 process.o
链接 main.o process.o 成 process[xiao@hcss-ecs-28ce dir_process]$ ls
main.c  main.o  Makefile  process  process.c  process.h  process.o
[xiao@hcss-ecs-28ce dir_process]$ ./process
hello world[xiao@hcss-ecs-28ce dir_process]$ make clean
[xiao@hcss-ecs-28ce dir_process]$ ls
main.c  Makefile  process.c  process.h

2. 第一次尝试(写代码就是:看效果 + 反复修改代码)

#include"process.h"    #define SIZE 101 //100个警号+\0    
#define STYLE '#'    
// v1: 展示进度条的基本功能    void process()    
{    int rate = 0;    char buffer[SIZE];    // 将buffer数组里的字符全部设置成\0                                                                             // 以免字符串的\0被覆盖,打印乱码    memset(buffer, 0, sizeof(buffer));    while(rate <= 100)    {    printf("[%s]\n", buffer);    buffer[rate] = STYLE;    rate++;    }    // printf("hello world\n");    
} 

在这里插入图片描述

  • 先解决第一个问题:这里打印成多行,只想让它在一行内增长警号的数量。这里就要用到倒计时小程序的思路:换行符( \r)+ fflush 刷新缓冲区
    在这里插入图片描述
  • 要解决上面这个问题也很简单:while循环结束后,打印换行符即可(printf("\n");
  • 是打印在一行了,但是一下就出现了一百个警号,好像没体现出动态增长。这里就要请出程序休眠函数:sleepusleepsleep函数参数的单位是秒,usleep函数参数的单位是微秒
    在这里插入图片描述
  • 可是最右边的中括号咋跟着一起走呢?可以用到:[%100s],预留出100个字符。这样中括号就在最右边,但你会发现它是倒着增长警号数量的(从右边逐渐增长)
  • 这是因为格式化输出定长控制的时候,不足对应的位置默认是右对齐的,所以100前面得加个负号
    在这里插入图片描述
#include"process.h"#define SIZE 101 //100个警号+\0
#define STYLE '#'
// v1: 展示进度条的基本功能void process()
{int rate = 0;char buffer[SIZE];// 将buffer数组里的字符全部设置成\0// 以免字符串的\0被覆盖,打印乱码memset(buffer, 0, sizeof(buffer));while(rate <= 100){printf("[%-100s]\r", buffer);fflush(stdout);buffer[rate] = STYLE;rate++;// sleep(1);这里用sleep函数也可以usleep(50000);// 1s = 1000ms = 1000x1000us}printf("\n");// printf("hello world\n");
}

调整比率(下载进度的) + 旋转光标

  • 这里的比率就是rate#的数量),有几个#就意味着下载了多少百分比
    在这里插入图片描述
  • 而最后面的 %% 是为了在输出中显示一个 % 符号本身,所以用了两个 % 。这样在输出时,这个格式字符串就会正确地显示一个 % 符号,而不是被当作格式说明符来解析
  • 如何让\字符旋转起来,这个思路设计的也很巧妙。一个字符串:"|/-\\",只要不断地依次打印其中地字符,就能模拟出动态旋转,\\\
    在这里插入图片描述
  • 动图展示效果(动图光标旋转刷新不出来)
    请添加图片描述

代码

#include"process.h"#define SIZE 101 //100个警号+\0
#define STYLE '#'
// v1: 展示进度条的基本功能void process()
{int rate = 0;char buffer[SIZE];// 将buffer数组里的字符全部设置成\0// 以免字符串的\0被覆盖,打印乱码memset(buffer, 0, sizeof(buffer));const char* label = "|/-\\";int len = strlen(label);while(rate <= 100){printf("[%-100s][%d%%][%c]\r", buffer, rate, label[rate%len]);fflush(stdout);buffer[rate] = STYLE;rate++;// sleep(1);这里用sleep函数也可以usleep(50000);// 1s = 1000ms = 1000x1000us}printf("\n");// printf("hello world\n");
}

咱们软件安装的时候,安装的进度条显示是根据你实际下载的数据量和期望下载的数据量之比去显示进度条的长度(边下载,边更新进度条),并不是向上面这种直接将进度条直接打完,才去开始下载。现在写的代码都是单进程的代码

第二个版本

1. 代码展示

  • total:下载的总量
  • speed[]:随机下载的速度
  • FlushProcess:根据目前下载的量,去更新一次进度
  • current:目前下载的量

process.c,记得在process.h文件中定义该函数的声明

#include"process.h"#define SIZE 101 //100个警号+\0
#define STYLE '#'// v2: 根据进度,动态刷新一次进度条void FlushProcess(double total, double current)
{char buffer[SIZE];memset(buffer, 0, sizeof(buffer));double rate = current*100.0/total;int num = (int)rate;//打印警号的数量// 根据下载的进度,刷新一次进度条for (int i = 0; i < num; i++)buffer[i] = STYLE;printf("[%-100s][%.1lf%%]\r", buffer, rate); //.1:保留一位小数fflush(stdout);if (num >= 100) printf("\n");
}

main.c

#include"process.h"
#include<time.h>
#include<stdlib.h>double total = 1024.0; //下载的总量;
double speed[] = {1.0, 0.5, 0.3, 0.2, 0.01, 0.001}; // 下载速度-网速void download()
{srand(time(NULL));double current = 0.0;while (current <= total){FlushProcess(total, current);//下面的代码是模拟资源的下载usleep(5000);current += speed[rand() % sizeof(speed)];}
}
int main()
{download();// 进行process的函数调用// process();return 0;
}

2. 问题

在这里插入图片描述

第一个解决方案:将int i = 0;放置在for循环的外面

在这里插入图片描述

第二个解决方案:编辑Makefile,编译加工-std=c99的选项。如果出现了一个warning的警告关于usleep函数,不用管是C99的问题

在这里插入图片描述

这里的current += speed并不一定能让current 等于total,可能会出现下面的两个问题(这里的rate=100.0,可能是四舍五入,导致if (num >= 100) printf("/n");语句不执行)

在这里插入图片描述
在这里插入图片描述

void download()
{srand(time(NULL));double current = 0.0;while (current <= total){FlushProcess(total, current);if (current >= total) break;//下面的代码是模拟资源的下载usleep(5000);current += speed[rand() % sizeof(speed)];// current 假如等于99,加上speed等于101,就跳出了循环// 那实际上current = 100,这次的进度条还没刷新if (current >= total) current = total;}
}

3. 添加旋转光标

旋转光标的存在原因:可能因为你网速的原因,那段时间的下载量不足以让警号和比率增长,那为了让用户看到实际上确实在下载,就需要用到旋转光标。只要调用了FlushProcess函数,光标就会去旋转

  • 这里可以在main.c中将current += speed注释掉就能看到效果。ctrl + c可以强制停止

在这里插入图片描述

4. 完整代码

4.1 main.c

#include"process.h"
#include<time.h>
#include<stdlib.h>double total = 1024.0; //下载的总量;
double speed[] = {1.0, 0.5, 0.3, 0.2, 0.01, 0.001}; // 下载速度-网速void download()
{srand(time(NULL));double current = 0.0;while (current <= total){FlushProcess(total, current);if (current >= total) break;//下面的代码是模拟资源的下载usleep(5000);current += speed[rand() % sizeof(speed)];if (current >= total) current = total;}
}
int main()
{download();// 进行process的函数调用// process();return 0;
}

4. 2 process.c

#include"process.h"#define SIZE 101 //100个警号+\0
#define STYLE '#'// v2: 根据进度,动态刷新一次进度条void FlushProcess(double total, double current)
{// 旋转光标const char* label = "|/-\\";int len = strlen(label);static int index = 0;char buffer[SIZE];memset(buffer, 0, sizeof(buffer));double rate = current*100.0/total;int num = (int)rate;//打印警号的数量// 根据下载的进度,刷新一次进度条int i = 0;for (; i < num; i++)buffer[i] = STYLE;printf("[%-100s][%.1lf%%][%c]\r", buffer, rate, label[index++]); //.1:保留一位小数fflush(stdout);index %= len;if (num >= 100) printf("\n");
}

5. 补充(解耦)

  • 想要这个进度条和实际的业务彻底解耦
  • 举个例子, 上面是用到下载资源这个业务上,那以后如果要用到上传或加载呢?那每一份上传,加载的进度条代码本身都需要把FlushProcess函数给带上吗?
  • 这样的代码是不是耦合度比较高,下面的代码就用到了回调的方式进行进度条的刷新
  • 假如我今天更改了FlushProcess函数名,用回调函数就不会对download的逻辑造成任何影响,只需要更改下download所传参数的函数名即可
#include"process.h"
#include<time.h>
#include<stdlib.h>//函数指针类型
typedef void (*call_t)(const char*,double,double);double total = 1024.0;
//double speed = 1.0;
double speed[] = {1.0, 0.5, 0.3, 0.02, 0.1, 0.01};//回调函数
void download(int total, call_t cb)
{srand(time(NULL));double current = 0.0;while(current <= total){cb("下载中", total, current); // 进行回调if(current>=total) break;// 下载代码int random = rand()%6;usleep(5000);current += speed[random];if(current>=total) current = total;}
}void uploadload(int total, call_t cb)
{srand(time(NULL));double current = 0.0;while(current <= total){cb("上传中", total, current); // 进行回调if(current>=total) break;// 下载代码int random = rand()%6;usleep(5000);current += speed[random];if(current>=total) current = total;}
}int main()
{download(1024.0, FlushProcess);printf("download 1024.0MB done\n");download(512.0, FlushProcess);printf("download 512.0MB done\n");download(256.0,FlushProcess);printf("download 256.0MB done\n");download(128.0,FlushProcess);printf("download 128.0MB done\n");download(64.0,FlushProcess);printf("download 64.0MB done\n");uploadload(500.0, FlushProcess);return 0;
}
http://www.xdnf.cn/news/14548.html

相关文章:

  • Redis 分布式锁、红锁分别是什么?红锁有什么问题?
  • 说说什么是幂等性?
  • 神经网络中的交叉熵(Cross-Entropy)损失函数详解
  • 【知识图谱提取】【阶段总结】【LLM4KGC】LLM4KGC项目提取知识图谱推理部分
  • 57-Oracle SQL Profile(23ai)实操
  • nginx服务器配置时遇到的一些问题
  • Mac电脑-触摸板增强工具-BetterTouchTool
  • 文本分类与聚类:让信息“各归其位”的实用方法
  • 力扣网C语言编程题:多数元素
  • MCPServer编程与CLINE配置调用MCP
  • 1.23Node.js 中操作 mongodb
  • 【Linux-shell】探索Dialog 工具在 Shell 图形化编程中的高效范式重构
  • 让大模型“更懂人话”:对齐训练(RLHF DPO)全流程实战解析
  • 蓝牙 5.0 新特性全解析:传输距离与速度提升的底层逻辑(面试宝典版)
  • 下载链接记录
  • 【Flink实战】 Flink SQL 中处理字符串 `‘NULL‘` 并转换为 `BIGINT`
  • Vue学习笔记
  • 二分查找----1.搜索插入位置
  • C/C++ 高频八股文面试题1000题(一)
  • ROS1/ROS2中工作空间和工作包创建详解
  • 服务网格安全(Istio):用零信任架构重构微服务通信安全
  • 5.3 LED字符设备驱动
  • 深度学习笔记27-LSTM实现糖尿病探索与预测(Pytorch)
  • 实验问题记录:PyTorch Tensor 也会出现 a = b 赋值后,修改 a 会影响 b 的情况
  • 解锁决策树:数据挖掘的智慧引擎
  • IT运维面试常问问题答案
  • QEMU学习之路(10)— RISCV64 virt 使用Ubuntu启动
  • 【C++】哈希表的实现(开放定址法)
  • 服务器手动安装并编译R环境库包:PROJ→RGDAL
  • C++ 11 智能指针 std::weak_ptr