【Linux旅行记】进度条小程序

news2025/2/23 23:27:13

文章目录

  • 一、预备知识
    • 1.1回车换行
    • 1.2缓冲区
  • 二、倒计时
  • 三、进度条
    • 3.1普通版本源代码
    • 3.2高级版本源代码
  • 🍀小结🍀

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

🎉博客主页:小智_x0___0x_

🎉欢迎关注:👍点赞🙌收藏✍️留言

🎉系列专栏:Linux入门到精通

🎉代码仓库:小智的代码仓库


一、预备知识

1.1回车换行

我们一般意义上的回车换行是两个概念,一般我们在C语言上的\n的作用是回车+换行,他把两个操作都做了,但是\转义字符也有别的的作用,我们一般回车是回车,换行是换行,两个是不同的概念。回车是指讲光标移动到当前行的最左侧开始位置,换行指的是光标水平方向保持不变,向下平移一行,C语言中有一个\r就是实现回车功能。
在这里插入图片描述

我们平时用的键盘上面有一个Enter的按键,这个按键就是实现了我们的回车+换行的作用。

1.2缓冲区

我们先来看一段代码:

#include "processBar.h"
#include <unistd.h>
int main()
{
    printf("hello world\n");
   
   	sleep(2);
    
    return 0;                                                                             
}  

这个程序是输出一个hello world 然后再让程序休眠2秒,这里的sleep函数是系统函数功能是让程序休眠指定时间,我们可以通过man 3 sleep手册来查看sleep函数的使用方法。

在这里插入图片描述

我们来运行这个代码来看看效果:
在这里插入图片描述

这里可以看到的现象是,程序先完成了打印hello world再休眠两秒钟。

我们再来改改代码:

#include "processBar.h"
#include <unistd.h>
int main()
{
    printf("hello world");
   
    sleep(2);
    return 0;
}

这次我们把\n去掉,此时程序会是怎么样的呢,先打印还是先休眠,我们一起来验证一下:

在这里插入图片描述

可以看到我们把\n去掉之后程序是先休眠了两秒,接着再在显示器上打印出hello world,这次没有换行符,所以bash命令行就会紧接在hello world的后面。

那么事实真的是这样的嘛?

我们可以来想一想,一个C语言程序是严格按照代码顺序从上往下依次执行的,不管怎样都是这样,肯定是先执行打印printf在进行sleep休眠2秒,但是此时又有一个疑问了,为什么我们printf的内容没有显示出来呢?在我sleep期间hello world在哪里?

一定被保存起来了,那么要保存就一定需要内存空间,这里其实就是保存在来我们的缓冲区里面,这里的缓冲区是由C语言维护的一段内存

所以当程序执行结束的时候才会将缓冲区中的内容刷新出来。

那我们要强制刷新呢?

这里我们就要知道一个C语言程序运行会默认帮我们打开这三个流:

  • stdin - - - -标准输入流(键盘)
  • stdout - - - - 标准输出流(显示器)
  • stderr - - - -标准错误流(显示器)

在我们平时使用文件操作的时候打开一个文件也是用的FILE *类型来打开文件。

那么我们要强制刷新就要用到这个函数fflush

在这里插入图片描述

这里刚好就是接收FILE* 类型。

具体操作:

#include "processBar.h"
#include <unistd.h>
int main()
{
    printf("hello world");
    fflush(stdout);
    sleep(2);
    return 0;
}

我们其实打印出的数据是往stdout中打印的,所以我们在printf后面紧接着一个fflush来强制刷新。

我们来运行看看现象:

在这里插入图片描述

可以看到这次我们是先显示出了hello warld再休眠两秒,bash的命令行才刷新出来。

总结:

  • \n可以刷新缓冲区
  • fflush可以强制刷新缓冲区
  • 程序结束可以刷新缓冲区

二、倒计时

有了上面的基础我们可以简单的来实现一个倒计时:

#include "processBar.h"
#include <unistd.h>
int main()
{
    int cnt=10;//定义倒计时时间
    while(cnt>=0)
    {
        printf("%-2d\r",cnt);//%-2d 表示以两个字符位输出并以左对齐方式输出  `\r`表示只回车不换行
        fflush(stdout);//强制刷新缓冲区
        cnt--;
        sleep(1);//程序休眠1s
    }
    printf("\n");

    return 0;
}

我们再来看看效果:

在这里插入图片描述

在这段代码中,%-2d\r的作用是实现倒计时效果。%-2d表示以两个字符位输出并以左对齐方式输出,%d是输出整数的占位符。\r表示回车到行首,即光标移到行首,而不换行。

因此,每次循环时,数字会被输出并覆盖上一次输出的数字,从而实现倒计时的效果。由于使用了\r回车到行首,所以数字输出在同一行上,不会换行。

另外,fflush(stdout)强制刷新缓冲区是为了确保每次输出都能够立即显示在屏幕上,而不是留在缓冲区中等待下一次输出。sleep(1)函数是让程序休眠1秒钟,以便实现倒计时效果。

三、进度条

设计思路:

  1. 确定进度条的显示方式,例如使用字符 ‘#’ 表示进度条的进度。
  2. 确定进度条的长度,例如设定进度条长度为100个字符。
  3. 计算进度条的进度,例如已完成任务的百分比为 50%,则进度条应该显示50个 ‘#’ 字符。
  4. 每次更新进度条时,先将光标移动到行首,然后输出当前进度条的状态,再将光标移回到行首,以便下一次更新。
  5. 可以使用定时器或者其他方式控制进度条的更新速度,例如每隔1秒更新一次进度条。

3.1普通版本源代码

processBar.h

#pragma once 
#include<stdio.h>
#define NUM 102 // 进度条长度
#define TOP 100 // 进度条最大值
#define BODY '=' // 进度条已完成部分的字符
#define RIGHT '>' // 进度条右边界的字符

// 进度条函数的声明
extern void processbar(int speed);

processBar.c

#include "processBar.h"
#include <string.h>
#include <unistd.h>
// 进度条的四种状态,即 |、/、-、\
const char *lable="|/-\\";

void processbar(int speed)
{
    char bar[NUM]; // 存储进度条的字符数组
    memset(bar,'\0',sizeof(bar)); // 初始化进度条数组
    int len = strlen(lable); // 计算进度条状态的长度
    int cnt = 0; // 进度条的当前值
    while(cnt <= TOP) // 当进度条的当前值小于等于最大值时,继续循环
    {
        printf("[%-100s][%d%%][%c]\r",bar,cnt,lable[cnt%len]); // 输出进度条信息
        fflush(stdout); // 刷新缓冲区,使得程序能够立即输出
        bar[cnt++]= BODY; // 将 BODY 字符加入到进度条数组中,并将当前值加1
        if(cnt<100) bar[cnt] = RIGHT; // 当进度条未达到100%时,在进度条的末尾加上 RIGHT 字符,防止有边界越界
        usleep(speed); // 程序休眠一段时间,以控制进度条的更新速度
    }
    printf("\n"); // 输出提示信息,任务已完成
}

main.c

#include "processBar.h"
#include <unistd.h>
int main()
{

    processbar(100000);
    
    return 0;
}

代码效果:
在这里插入图片描述

3.2高级版本源代码

在现实中进度条是表示我们下载某些文件的进度,所以进度不是由我们自己来决定的需要,所以我们需要写一个进度条接口来接收当前下载的百分比,进而通过调用函数来打印出当前的进度。

plus版本:

processBar.h

#pragma once 
#include<stdio.h>
#define NUM 102 // 进度条长度
#define TOP 100 // 进度条最大值
#define BODY '=' // 进度条已完成部分的字符
#define RIGHT '>' // 进度条右边界的字符

extern void processbar(int rate);

processBar.c

#include "processBar.h"
#include <string.h>
#include <unistd.h>

// 进度条的四种状态,即 |、/、-、\
const char *lable="|/-\\";

// 存储进度条的字符数组,初始化为0
char bar[NUM]={0};

void processbar(int rate)
{
    if(rate < 0 || rate > 100) return; // 判断进度条的值是否在合法范围内
    int len = strlen(lable); // 计算进度条状态的长度
    printf("[%-100s][%d%%][%c]\r",bar,rate,lable[rate%len]); // 输出进度条信息
    fflush(stdout); // 刷新缓冲区,使得程序能够立即输出
    bar[rate++]= BODY; // 将 BODY 字符加入到进度条数组中,并将当前值加1
    if(rate<100) bar[rate] = RIGHT; // 当进度条未达到100%时,在进度条的末尾加上 RIGHT 字符,防止有边界越界添加
}

main.c

#include "processBar.h"
#include <unistd.h>
int main()
{
    
    int total = 1000;//要下载的总进度
    int curr = 0;//初始进度

    while(curr <= total)
    {
         processbar(curr*100/total);
         curr+=10;//每次下载10
         usleep(50000);//模拟下载花费的时间
    }
    printf("\n");
	return 0;
}

plusplus版本:

processBar.h

#pragma once 
#include<stdio.h>
#define NUM 102 // 进度条长度
#define TOP 100 // 进度条最大值
#define BODY '=' // 进度条已完成部分的字符
#define RIGHT '>' // 进度条右边界的字符

// 进度条函数的声明
extern void processbar(int rate);
extern void initbar();

processBar.c

#include "processBar.h"
#include <string.h>
#include <unistd.h>
// 定义了一些控制台输出颜色的宏
#define NONE "\033[m"
#define RED "\033[0;32;31M"
#define GREEN "\033[0;32;32m"
#define LIGHT_BLUE "\033[1;34m"
#define LIGHT_PURPLE "\033[1;35m"

// 进度条的四种状态,即 |、/、-、\
const char *lable="|/-\\";

// 存储进度条的字符数组,初始化为0
char bar[NUM]={0};

// 进度条函数的具体实现部分,实现了进度条的显示、刷新、更新等功能
void processbar(int rate)
{
    if(rate < 0 || rate > 100) return; // 判断进度条的值是否在合法范围内
    if(rate==0) initbar(); // 如果进度条为0,则初始化进度条数组
    int len = strlen(lable); // 计算进度条状态的长度
    printf("["LIGHT_BLUE"%-100s"NONE"]""[%d%%][%c]\r",bar,rate,lable[rate%len]); // 输出进度条信息,带有颜色
    fflush(stdout); // 刷新缓冲区,使得程序能够立即输出
    bar[rate++]= BODY; // 将 BODY 字符加入到进度条数组中,并将当前值加1
    if(rate<100) bar[rate] = RIGHT; // 当进度条未达到100%时,在进度条的末尾加上 RIGHT 字符,以便显示进度条的右边界
}

// 初始化进度条数组
void initbar()
{
    memset(bar, '\0', sizeof(bar));
}

main.c

#include "processBar.h"
#include <unistd.h>

// 定义了一个函数指针类型 callback_t
typedef void (*callback_t)(int);

// 模拟一种安装或者下载的任务
void downLoad(callback_t cb)
{
    int total = 1000; // 总大小为1000MB
    int curr = 0;     // 当前下载大小为0MB

    while(curr <= total)
    {
        usleep(50000);  // 模拟下载花费的时间
        int rate = curr*100/total; // 计算当前下载进度

        cb(rate); // 通过回调函数展示进度

        curr += 10;     // 循环下载了一部分
    }
    printf("\n"); // 输出提示信息,任务已完成
}

int main()
{
    printf("donwnload 1: \n");
    downLoad(processbar); // 下载任务1,回调函数为 processbar
    printf("donwnload 2: \n");
    downLoad(processbar); // 下载任务2,回调函数为 processbar
    printf("donwnload 3: \n");
    downLoad(processbar); // 下载任务3,回调函数为 processbar
    printf("donwnload 4: \n");
    downLoad(processbar); // 下载任务4,回调函数为 processbar
    
    return 0;
}

上面的代码实现了一个简单的下载任务,并通过回调函数 processbar 实现了下载进度的显示。代码主要分为以下几个部分:

  1. 头文件部分,包含了 stdio.h 头文件和 processBar.h 头文件,以及一些宏定义。

  2. 进度条函数的声明部分,声明了进度条函数 processbar 和初始化进度条数组的函数 initbar

  3. 进度条函数的具体实现部分,实现了进度条的显示、刷新、更新等功能。这部分代码和之前相同。

  4. 初始化进度条数组的函数 initbar 的具体实现部分。这个函数只是简单地将进度条数组清零。

  5. 主函数部分,模拟了四个下载任务,并通过回调函数 processbar 展示下载进度。具体来说,这部分代码主要做了以下几件事情:

    • 调用 downLoad 函数模拟四个下载任务,并将回调函数设置为 processbar

    • 在每个下载任务开始时输出提示信息。

    • 在每个下载任务结束时输出提示信息。

代码运行效果:
在这里插入图片描述

🍀小结🍀

今天我们学习了"Linux进度条小程序"相信大家看完有一定的收获。种一棵树的最好时间是十年前,其次是现在! 把握好当下,合理利用时间努力奋斗,相信大家一定会实现自己的目标!加油!创作不易,辛苦各位小伙伴们动动小手,三连一波💕💕~~~,本文中也有不足之处,欢迎各位随时私信点评指正!
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/897481.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据可视化工具CEETRON Envision助力ESTECO客户实现CAE数据转化

​行业&#xff1a;制造业&#xff1b;工业设计&#xff1b;汽车&#xff1b;航天 挑战&#xff1a;工业客户需要有效的方法来解释 CAE 数据&#xff1b;ESTECO 寻求提供 CAE 可视化功能来帮助客户做出决策&#xff1b;许多可用的可视化工具无法提供对模型中数据的完全访问以进…

java练习5 爬楼梯

题目:小谢 喜欢爬楼梯,有时一次爬1阶,有时一次爬2阶. 如果,楼梯有20阶,小明一共有多少次爬法. public static void main(String[] args) {System.out.println(getNumber(20));}public static int getNumber(int n){if(n1){return 1;}if (n2){return 2;}return getNumber(n-1)ge…

Docker vs. Kubernetes:选择合适的场景

在决定使用 Docker 还是 Kubernetes 之前&#xff0c;让我们看看一些实际的场景&#xff0c;以便更好地理解它们的适用性。 使用 Docker 的场景 假设您正在开发一个微服务应用程序&#xff0c;其中每个微服务都需要一些特定的依赖项和环境。在这种情况下&#xff0c;Docker 是一…

218、仿真-基于51单片机步进电机正反转加减速度LCD1602显示Proteus仿真设计(程序+Proteus仿真+配套资料等)

毕设帮助、开题指导、技术解答(有偿)见文未 目录 一、硬件设计 二、设计功能 三、Proteus仿真图​编辑 四、程序源码 资料包括&#xff1a; 需要完整的资料可以点击下面的名片加下我&#xff0c;找我要资源压缩包的百度网盘下载地址及提取码。 方案选择 单片机的选择 方…

shell脚本三剑客之awk

awk文本三剑客之一&#xff0c;是功能最强大的文本工具 awk也是按行来进行操作&#xff0c;对行操作完之后&#xff0c;可以根据指定命令来对行取列 awk的分隔符&#xff1a;默认分隔符是空格或者tab键&#xff0c;多个空格&#xff0c;会自动压缩成一个 用法&#xff1a; …

iPhone上的个人热点丢失了怎么办?如何修复iPhone上不见的个人热点?

个人热点功能可将我们的iPhone手机转变为 Wi-Fi 热点&#xff0c;有了Wi-Fi 热点后就可以与附近的其他设备共享其互联网连接。 一般情况下&#xff0c;个人热点打开就可以使用&#xff0c;但也有部分用户在升级系统或越狱后发现 iPhone 的个人热点消失了。 iPhone上的个人热点…

SpringBoot + Vue 微人事(十二)

职位批量删除实现 编写后端接口 PositionController DeleteMapping("/")public RespBean deletePositionByIds(Integer[] ids){if(positionsService.deletePositionsByIds(ids)ids.length){return RespBean.ok("删除成功");}return RespBean.err("删…

11. Vuepress2.x 关闭夜间模式

修改 docs/.vuepress/config.ts 配置文件 设置 themeConfig.darkMode属性详见 官网 module.exports {host: localhost, // ipport: 8099, //端口号title: 我的技术站, // 设置网站标题description: 描述&#xff1a;我的技术站,base: /, //默认路径head: [// 设置 favor.ico&a…

LVGL基本控件介绍

1. 弧(lv_arc) 特点 弧的0度在右边&#xff0c;90度在下边 效果图 源码 void lv_arc_demo(void) {/* Create an Arc */lv_obj_t* arc lv_arc_create(lv_scr_act(), NULL);/* Set Background range */lv_arc_set_bg_angles(arc, 0, 360);/* Set Forward range */lv_arc_set…

探索心律失常:病因、诊断与治疗以及与肠道菌群的关联

谷禾健康 你是否有时会感到心悸、心慌、胸闷、气短、头晕、乏力&#xff1f;你是否有时感觉自己的心跳过快或过慢&#xff1f; 如果有上述情况&#xff0c;就要引起重视了&#xff0c;你可能存在心律失常。心律失常是最常见的心脏疾病之一&#xff0c;涉及到心脏的电活动节奏异…

JAVA设计模式总结之23种设计模式

一、什么是设计模式 设计模式&#xff08;Design pattern&#xff09;是一套被反复使用、多数人知晓的、经过分类编目的、代码设计…

c++ | 字节转换 | 字长 | 机器位数

为什么有的时候脑子转不过来&#xff1f;&#xff1f; 为什么要对字节、机器长啊、位啊都要门清 位数 一般的就是指计算机的位数&#xff0c;比如64位/32位&#xff0c;更简单的理解&#xff0c;计算机就是在不停的做二进制的计算&#xff0c;比如32位计算机&#xff0c;在长…

Day14 01-Shell脚本编程详解

文章目录 第一章 Shell编程【重点】1.1. Shell的概念介绍1.1.1. 命令解释器4.1.1.2. Shell脚本 1.2. Shell编程规范1.2.1. 脚本文件的结构1.2.2. 脚本文件的执行 1.3. Shell的变量1.3.1. 变量的用法1.3.2. 变量的分类1.3.3. 局部变量1.3.4. 环境变量1.3.5. 位置参数变量1.3.6. …

Android 远程真机调研

背景 现有的安卓测试机器较少&#xff0c;很难满足 SDK 的兼容性测试及线上问题&#xff08;特殊机型&#xff09;验证&#xff0c;基于真机成本较高且数量较多的前提下&#xff0c;可以考虑使用云测平台上的机器进行验证&#xff0c;因此需要针对各云测平台进行调研、比较。 …

四川天蝶电子商务有限公司:新媒体+短视频运营

随着互联网科技的不断发展&#xff0c;新媒体行业如今已经成为了人们获取信息、进行交流的主要渠道之一。在这样的大环境下&#xff0c;短视频成为了新媒体运营的一个重要组成部分。那么&#xff0c;新媒体短视频运营到底是做什么呢&#xff1f;接下来&#xff0c;我们将从几个…

猿辅导Motiff亮相IXDC 2023国际体验设计大会,发布新功能获行业高度关注

近日&#xff0c;“IXDC 2023国际体验设计大会”在北京国家会议中心拉开序幕&#xff0c;3000设计师、1000企业、200全球商业领袖&#xff0c;共襄为期5天的用户体验创新盛会。据了解&#xff0c;此次大会是以“设计领导力”为主题&#xff0c;分享全球设计、科技、商业的前沿趋…

新能源电动车充电桩控制主板接口

新能源电动车充电桩控制主板接口 你是否想过&#xff0c;为什么你的手机在充电时总是烫得吓人?为什么你的电动车在充电时总是那么慢?这一切&#xff0c;都与充电桩控制主板接口有关! 充电桩控制主板接口必须具备以下特点&#xff1a; 1. 多种输入接口&#xff1a;充电桩主板应…

VisualGLM-6B:一个基于ChatGLM-6B模型的图像理解模型

介绍 VisualGLM-6B 是一个开源的&#xff0c;支持图像、中文和英文的多模态对话语言模型&#xff0c;语言模型基于 ChatGLM-6B&#xff0c;具有 62 亿参数&#xff1b;图像部分通过训练 BLIP2-Qformer 构建起视觉模型与语言模型的桥梁&#xff0c;整体模型共78亿参数。 Visua…

如何快速学习AdsPower RPA

之前写过如何学习AdsPower RPA的系列文章&#xff0c;有些同学可能觉得学习路径太慢了&#xff0c;希望能更快一些。于是就有了今天这篇文章。 自从我踩过不少编写RPA流程的坑之后&#xff0c;再反过头来研究模板商店里面的模板&#xff0c;最后发现这才是好东西。虽然模板商店…

Python入门【动态添加属性和方法、正则表达式概述、match函数的使用、常用匹配符、限定符 、限定符使用示例】(二十九)

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是爱敲代码的小王&#xff0c;CSDN博客博主,Python小白 &#x1f4d5;系列专栏&#xff1a;python入门到实战、Python爬虫开发、Python办公自动化、Python数据分析、Python前后端开发 &#x1f4e7;如果文章知识点有错误…