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

【Linux 学习计划】-- 倒计时、进度条小程序

目录

\r 、\n、fflush

倒计时

进度条

进度条进阶版

结语


\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;
}

结语

这篇文章到这里就结束啦!!~( ̄▽ ̄)~*

如果觉得对你有帮助的,可以多多关注一下喔

http://www.lryc.cn/news/2386367.html

相关文章:

  • 微服务的应用案例
  • 后端开发概念
  • 2025网络安全趋势报告 内容摘要
  • 云原生安全基石:深度解析HTTPS协议(从原理到实战)
  • Autodl训练Faster-RCNN网络--自己的数据集(一)
  • python打卡day36
  • 8.Java 8 日期时间处理:从 Date 的崩溃到 LocalDate 的优雅自救​
  • 基于Python的全卷积网络(FCN)实现路径损耗预测
  • 【ubuntu】安装NVIDIA Container Toolkit
  • Paimon和Hive相集成
  • 精益数据分析(74/126):从愿景到落地的精益开发路径——Rally的全流程管理实践
  • HarmonyOS 鸿蒙应用开发进阶:深入理解鸿蒙跨设备互通机制
  • Vue.js教学第十五章:深入解析Webpack与Vue项目实战
  • 深入浅出 Python Testcontainers:用容器优雅地编写集成测试
  • Cmake编译gflags过程记录和在QT中测试
  • 项目中Warmup耗时高该如何操作处理
  • 制作一款打飞机游戏53:子弹样式
  • Windows磁盘无法格式化及磁盘管理
  • 每日算法 -【Swift 算法】Z 字形变换(Zigzag Conversion)详解与实现
  • Docker运维-5.3 配置私有仓库(Harbor)
  • day 36
  • mybatis-plus使用记录
  • Mcu_Bsdiff_Upgrade
  • 有监督学习——决策树
  • 华为OD机试真题——启动多任务排序(2025B卷:200分)Java/python/JavaScript/C/C++/GO最佳实现
  • AWS云与第三方通信最佳实践:安全、高效的数据交互方案
  • Ubuntu Server 24 设置 WiFi 网络的方案
  • 【redis】redis和hiredis的基本使用
  • 大模型时代,Python 近红外光谱与 Transformer 模型:学习的必要性探究
  • 产品经理常用术语大全