linux--进度条

2023-09-20 09:43:41


本篇文章我们将来尝试写一个简单的小程序–进度条。

搭建环境

在这之前,我们要先搭建好一个框架,如下:

先依次创建好一个头文件(pb.h)用以声明和定义宏,一个源文件(pb.c)用于书写进度条源代码,一个源文件(main.c)用于测试以及后续搭建实际的场景,以及makefile。

makefile:

版本1

我们先从最简单的版本开始讲起,这个版本的进度条只是以单独的一个函数形式呈现出来,主要是为了先搞清楚程序的运行原理。

进度条一般是出现在下载任务,加载任务的某些场景中的,我们用rate来模拟当前下载的进度,MAX_RATE来模拟总进度,我们先创建一个字符数组用来存放我们要打印的进度条,再把该数组全部初始化为’\0’,然后循环打印,每次打印休眠一段时间,模拟下载所需的时间,最后初代形式就如下图所示:

我们发现打印的结果如下图所示:
在这里插入图片描述

这很明显不符合我们的预期,我们预期的是在同一行不断增加字符长度而不是往下每一行重新打印,所以我们要把\n换成\r,\r的作用是使光标回到当前行的开头。修改后的结果如下:

打印的结果如下:
在这里插入图片描述

我们发现,光标位置是不会往下走了,但是什么东西都没有打印出来,这是因为数据都被放在缓冲区了,我们需要用fflush(stdout)刷新一下输出流,把在缓冲区的东西在终端上打印出来
由于sleep时间过短,不好观察现象,我们就使用vim环境特有的库函数usleep,它的数字单位是微秒,需要引用的头文件是#include <unistd.h>,同时我们也可以用宏来定义这些变量以便于后续修改。为了美观和便于观察,我们还可以将当前下载进度百分比和旋转图标打印出来,图标我们用一个str数组保存下来,然后不断循环打印数组里的图标,所以第一个版本的完整代码如下:

版本2

接下来我们来讲版本2,我们通过版本2要将进度条与实际的情况联系起来,在版本1中,我们只能在函数体内运行我们的进度条,但真实情况是,我们每传递一次进度,这个进度条函数就在自己内部打印一次,然后再更新一次进度参数,进度条函数再自己打印一次,这样的话,我们就应该在外部进行循环打印,而进度条函数应该只是作为一个接口,所以我们首先就要将循环搬到函数外去,这样的话,我们就不能再像版本1一样初始化bar数组了,因为每次进入这个函数,都会重新初始化,不会保存上一次运行结束后的样子,所以我们用static修饰字符数组,使他出了自己的范围也不会被销毁。然后为了贴合实际工程,我们用一个头文件来存放头文件 宏 以及函数的声明,一个源文件来书写进度条函数,再用一个源问件来模拟实景场景进行测试。

process.h
在这里插入图片描述

main.c
在这里插入图片描述

process.c
在这里插入图片描述

这样写固然能实现功能,但我们实际情况是有一个总的资源大小,然后每过一段时间会下载一定的资源,我们把这两个数相除来表示当前进度作为参数传给process_v2函数,所以我们还可以优化成这样:

但这样还不是我们版本2的最终形态,因为在实际场景中,我们是会进行某种任务的通知,动态更新进度条,这里就要使用到回调函数,回调函数的使用和声明如下:

再结合process_v2,这就是版本2的完整代码。

版本3

版本3就是在版本2的基础上再做一些美化,真实情况下,进度条一般不会是#而是类似于箭头的样子,我们还可以为进度条添加颜色,C语言是支持此类语法的,感兴趣的小伙伴可以去搜一下,这里就不再过多赘述了。

功能上也有一些优化,我们一般在下载卡住的时候,进度没有动而图标应该是一直在旋转的,意思是当前进度虽然卡住了但实际上还在继续下载,版本2和版本1都没法实现这样的功能,所以我们要切断str数组打印与否和rate的联系,单独拿出来循环打印,具体实现如下:
在这里插入图片描述

所以最终完整代码如下:
makefile:

process:process.c main.c
	gcc -o $@ $^
.PHONY:clean
clean:
	rm -f process

main.c

void download(callback_t cb)
{
    int testcnt = 100;
    int target = TARGET_SIZE;
    int total = 0;

    while(total <= target)
    {
        usleep(STIME); // 用简单的休眠时间,模拟本轮下载花费的时间
        total += DSIZE;
        double rate = total*100.0/target;
        if(rate > 50.0 && testcnt) {
            total = target/2;
            testcnt--;
        }
        cb(rate); // 回调函数
    }
    cb(MAX_RATE); // 回调函数
    printf("\n");
}


// 下载的软件
int main()
{
    download(process_v3);
    return 0;
}

process.c

void process_v3(double rate)
{
    // version 2
    // TODO
    static char bar[SIZE]= {0};
    static int cnt = 0;
    int num = strlen(str);
    if(rate <= MAX_RATE && rate >=0)
    {
        cnt++;
        cnt = (cnt >= num ? 0 : cnt); //cnt %= num;
        printf("加载中... [\033[31;44m%-100s\033[0m][%.1f%%][%c]\r", bar, rate, str[cnt]);
        fflush(stdout);
        if(rate < MAX_RATE)
        {
            bar[(int)rate] = STYLE_BODY; //'='
            bar[(int)rate+1] = STYLE_HEADER; //'>'
        }
        else
        {
            bar[(int)rate] = STYLE_BODY;
        }
    }
    //if(rate == MAX_RATE) memset(bar, '\0', sizeof(bar));
}

process.h

#pragma once

#include <stdio.h>
#include <string.h>
#include <unistd.h>

#define SIZE 101
#define MAX_RATE 100
#define STYLE '#'
#define STIME 1000*40

#define STYLE_BODY '='
#define STYLE_HEADER '>'

typedef void (*callback_t)(double);

void process_v3(double);

以上就是关于进度条三个版本的全部内容(*≧ω≦)

更多推荐

Python异步编程|ASGI 与 Django(附源码)

异步服务网关接口(AsynchronousServerGatewayInterface,ASGI)秉承WSGI统一网关接口原则,在异步服务、框架和应用之间提供一个标准接口,同时兼容WSGI。01、ASGIASGI是根据统一接口的思想重新设计的新标准,你可能会有疑问,为什么不直接升级WSGI而去创造新的标准呢?WSGI是

以小见大,彻底理解 cookie,session,token 之间的关系,通俗易懂

发展史1、很久很久以前,Web基本上就是文档的浏览而已,既然是浏览,作为服务器,不需要记录谁在某一段时间里都浏览了什么文档,每次请求都是一个新的HTTP协议,就是请求加响应,尤其是我不用记住是谁刚刚发了HTTP请求,每个请求对我来说都是全新的。这段时间很嗨皮2、但是随着交互式Web应用的兴起,像在线购物网站,需要登录的

Java关于AbstractProcessor的使用

文章目录Step1项目准备Step2开发一个自定义的AbstractProcessorStep3DebugAbstractProcessor替换源码内容运行SpringBoot应用测试源码内容是否成功替换背景:我们都知道,在正常情况下,我们无法去变更二方,三方包中源码的Java文件的内容,但是在某些场景下,我们又希望可

Android Jetpack Compose之状态持久化与恢复

目录1.概述2.实例解析4.Compose提供的MapSaver和ListSaver4.1mapServer4.2ListSaver1.概述在之前的文章中,我们提到了remember,我们都知道remember可以缓存创建状态,避免因为重组而丢失。使用remember缓存的状态虽然可以跨越重组,但是不能跨Activit

热门免费api接口:含物流api,短信api,天气api等

热门免费api接口:含物流api,短信api,天气api。。。全国快递物流查询:目前已支持600+快递公司的快递信息查询。自动识别快递公司及单号,服务器毫秒响应,数据及时准确。通知短信:短信通知支持三大运营商以及虚拟运营商。短信验证码:支持三大运营商,支持大容量高并发。语音验证码短信:拨打电话告知用户验证码,实现信息验

【C++】动态内存管理 ③ ( C++ 对象的动态创建和释放 | new 运算符 为类对象 分配内存 | delete 运算符 释放对象内存 )

文章目录一、C++对象的动态创建和释放1、C语言对象的动态创建和释放的方式2、C++语言对象的动态创建和释放的方式二、代码示例-对象的动态创建和释放一、C++对象的动态创建和释放使用C语言中的malloc函数可以为类对象分配内存;使用free函数可以释放上述分配的内存;使用C++语言中的new运算符也可以为类对象分配内

【C++】动态内存管理 ② ( new 运算符 为 基础数据类型 / 基础数据数组类型 分配堆内存 )

文章目录一、C++对象的动态创建和释放二、new运算符为基础数据类型/基础数据数组类型分配堆内存1、语法说明2、语法简单示例3、代码示例-基础类型内存分配4、代码示例-基础数组类型内存分配三、完整代码示例-new运算符为基础数据类型/基础数据数组类型分配堆内存一、C++对象的动态创建和释放动态内存管理在C++语言中,就

浅谈C++|多态篇

1.多态的基本概念多态是C++面向对象三大特性之一多态分为两类1.静态多态:函数重载和运算符重载属于静态多态,复用函数名·2.动态多态:派生类和虚函数实现运行时多态静态多态和动态多态区别:·静态多态的函数地址早绑定–编译阶段确定函数地址·动态多态的函数地址晚绑定–运行阶段确定函数地址下面通过案例进行讲解多态动态多态满足

MySQL之优化SELECT语句

MySQL之优化SELECT语句文章目录MySQL之优化SELECT语句摘要:引言:1.MySQL性能提成优化概述2.WHERE子句优化3.范围优化4.哈希联接优化5.储存引擎下的优化6.索引条件下推优化7.嵌套循环联接算法8.嵌套联接优化(JOIN)总结:摘要:本文主题为MySQL优化SELECT语句,涵盖了数据库性

MySQL什么情况下会死锁,发生了死锁怎么处理呢?

🏆作者简介,黑夜开发者,CSDN领军人物,全栈领域优质创作者✌,CSDN博客专家,阿里云社区专家博主,2023年6月CSDN上海赛道top4。🏆数年电商行业从业经验,历任核心研发工程师,项目技术负责人。🏆本文已收录于PHP专栏:MySQL的100个知识点。🎉欢迎👍点赞✍评论⭐收藏文章目录🚀一、前言-关于数据

数据库 MVCC 详解

目录1.什么是MVCC?2.MVCC的好处?3.快照读?当前读分别是什么?怎么理解?3.1快照读3.2当前读4.MVCC实现原理4.1隐藏字段4.2undolog(版本链)4.3readView5.readView深层详解6.数据库的四种隔离级别7.读已提交和可重复读的区别?7.1MVCC主要作用体现在读已提交和可重复

热文推荐