Linux---(七)Makefile写进度条(三个版本)

简介: Linux---(七)Makefile写进度条(三个版本)


一、前提引入

🎗️下面的代码什么现象?

🎗️现象:马上打印出Hello Makefile!

🎗️下面的代码什么现象?

🎗️现象:停留三秒后打印出Hello Makefile!

现象是先sleep!但是一定是printf先执行,printf早就执行了,只不过字符串没有被显示出来罢了。所以在sleep期间,字符串在哪里?

答案:在输出缓冲区。

二、缓冲区

🎗️C/C++语言,会针对标准输出,给我们提供默认的缓冲区。

🎗️输出缓冲区在哪里呢?

在标准输出流。C/C++默认会打开三个文件流:标准输入流、标准输出流、标准错误流。

🎗️fflush(stdout) 刷新缓冲区

🎗️\n 是一种刷新的策略 行刷新

C程序是默认有输出缓冲区的,数据输出时会默认放在输出缓冲区。之所以可以立马见到数据,是因为该数据被刷新了;如果没有立马见到它,该数据没有被刷新,被暂存在输出缓冲区stdout当中。强制刷新fflsh(stdout)可使数据立马显示出来。

三、回车换行

🎗️注意

回车换行是两个动作

回车是让光标回到该行的最开始位置

换行是换到下一行

🎗️图解

🎗️老式回车键造型(意思是充当两个动作)

🎗️\r 和 \n

🎗️在C语言中,\r代表回车,\n代表换行

🎗️在Linux中,\r代表回车,\n代表回车和换行

🎗️倒计时代码

代码1

效果

形成一个9到1的倒计时效果(打印完一个数字,回车回到最开始打印,下一个数字覆盖上一个数字的打印结果)

注意:如果没有刷新缓冲区那句代码,那么什么也不会显示出来。

代码2

🎗️效果

形成一个从100的倒计时

🎗️注意点

控制两位字符的输出宽度,可达到10的倒计时效果;控制三位字符的输出宽度,可达到100的倒计时效果。控制几位字符就在%d的d前面加数字。

输出结果如果不设置对齐方式,可能会出现覆盖从而达不到预想效果。输出结果左对齐,在%后加上-

思考

🎗️我们向显示器打印的数字真的是数字吗?

答案:不是

往显示器输出123,实际上输出的是1字符,2字符,3字符。

显示器只能显示字符。

因此显示器叫做显示器字符设备。

🎗️我们从键盘上读取到的内容,是什么呢?

b比如说我们从键盘输入1234,键盘读取到的并不是一千两百三十四,而是1字符、2字符、3字符、4字符,然后由scanf将字符串转成整数,放到对应的变量里,这样才有了整数。

四、进度条

预想的进度条效果

构想代码结构

(一)简单原理版本

Makefile

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

process.c

#include "process.h"
#include<string.h>
#include<unistd.h>
#define SIZE 101
#define MAX_RATE 100
#define STYLE '#'
#define STIME 1000*40
const char *str="|/-\\";
void process(){
    int rate=0;
    char bar[SIZE];
    memset(bar,'\0',sizeof(bar));
    int num=strlen(str);
    while(rate<=MAX_RATE){
        printf("[%-100s][%d%%][%c]\r",bar,rate,str[rate%num]);
        fflush(stdout);
        usleep(STIME);  
        bar[rate++]=STYLE;
    }
    printf("\n");
}

process.h

#pragma once 
#include<stdio.h>
void process();

main.c

#include "process.h"
int main(){
    process();
    return 0;
}

重点代码解读

进度条效果

(二)实际工程实践版本

下载逻辑

Makefile

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

process.c

#include "process.h"
#include<string.h>
#include<unistd.h>
const char *str="|/-\\";
void process_v2(int rate){
    static char bar[SIZE]={0};
    int num=strlen(str);
    if(rate<=MAX_RATE&&rate>=0){
        printf("[%-100s][%d%%][%c]\r",bar,rate,str[rate%num]);
        fflush(stdout);
        bar[rate]=STYLE;
    }
    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
void process_v2(int);

main.c

#include "process.h"
#define TARGET_SIZE 1024*1024
#define DSIZE 1024*10
void download(){
    int target=TARGET_SIZE;
    int total=0;
    while(total<target){
        usleep(STIME);
        total+=DSIZE;
        process_v2(total*100/target);
    }
    printf("\n");
}
//下载的软件
int main(){
    download();
    return 0;
}

重点代码解读

改进

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
typedef void(*callback_t)(int);
void process_v2(int);

main.c

#include "process.h"
#define TARGET_SIZE 1024*1024
#define DSIZE 1024*10
void download(callback_t cb){
    int target=TARGET_SIZE;
    int total=0;
    while(total<target){
        usleep(STIME);
        total+=DSIZE;
        int rate=total*100/target;
        cb(rate);
    }
    printf("\n");
}
//下载的软件
int main(){
    download(process_v2);
    return 0;
}

🎗️回调:把一段可执行的代码像参数传递给其他代码,而这段代码会在某个时刻被调用执行,这就叫做回调。

🎗️改进的版本:

这里,将更新显示的进度条的函数process_v2作为参数传递给download函数,在download函数中需要它时就调用它,调用以函数指针的形式来实现回调函数。

总结

版本2进度条不是在进度条函数内部进行循环打印的,这样有点不好,所以我们采用回调的方式,来进行某种任务的通知,动态更新进度条!(在下载任务中调用进度条)

(三)简单的美化风格

Makefile

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

process.c

#include "process.h"
#include<string.h>
#include<unistd.h>
const char *str="|/-\\";
void process_v3(double rate){
    static char bar[SIZE]={0};
    int num=strlen(str);
    if(rate<=MAX_RATE&&rate>=0){
        printf("[%-100s][%.1f%%][%c]\r",bar,rate,str[(int)rate%num]);
        fflush(stdout);
        if(rate<MAX_RATE){
            bar[(int)rate]=STYLE_BODY;
            bar[(int)rate+1]=STYLE_HEAD;
        }else{
            bar[(int)rate]=STYLE_BODY;
        }
    }
}

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_HEAD '>'
typedef void(*callback_t)(double);
void process_v3(double);

main.c

#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_HEAD '>'
typedef void(*callback_t)(double);
void process_v3(double);

进度条效果

模拟加载中效果

版本3基础上,模拟实现进度条加载过程中不移动时,数字不改变时,(即现实中加载的资源不足以1%时),后面的光标一直旋转(显示加载中)的效果。

🎗️在process.c和main.c文件中做了修改

process.c
#include "process.h"
#include<string.h>
#include<unistd.h>
const char *str="|/-\\";
void process_v3(double rate){
    static char bar[SIZE]={0};
    int num=strlen(str);
    static int cnt=0;
    if(rate<=MAX_RATE&&rate>=0){
        cnt++;
        cnt=(cnt>=num?0:cnt);
        printf("[%-100s][%.1f%%][%c]\r",bar,rate,str[cnt]);
        fflush(stdout);
        if(rate<MAX_RATE){
            bar[(int)rate]=STYLE_BODY;
            bar[(int)rate+1]=STYLE_HEAD;
        }else{
            bar[(int)rate]=STYLE_BODY;
        }
    }
}
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_HEAD '>'
typedef void(*callback_t)(double);
void process_v3(double);
main.c
#include "process.h"
#define TARGET_SIZE 1024*1024
#define DSIZE 1024*10
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/target;
        if(rate>50.0&&testcnt){
            total=target/2;
            testcnt--;    
         }
    cb(rate);
    }
    cb(MAX_RATE);
    printf("\n");
}
//下载的软件
int main(){
    download(process_v3);
    return 0;
}
Makefile
process:process.c main.c
  gcc -o $@ $^
.PHONY:clean
clean:
  rm -rf process
重点代码解读

进度条效果

C语言扩展–给进度条带上颜色

C语言有具体的语法可以设置不同的颜色,具体内容大家可以自行搜索,为进度条设置自己喜爱的颜色。


本篇内容的学习就到这里啦!如果对友友们有帮助的话,可以关注后续的创作内容哦~👻

相关文章
|
8月前
|
Ubuntu Linux
Ubuntu 23.04 用上 Linux 6.2 内核,预计下放到 22.04 LTS 版本
Linux 6.2 带来了多项内容更新,修复了 AMD 锐龙处理器设备在启用 fTPM 后的运行卡顿问题,还增强了文件系统。
|
8月前
|
Ubuntu Linux
Ubuntu24.04LTS默认采用Linux 6.8内核,实验性版本可通过PPA获得
IT之家提醒,当下的 Ubuntu 23.10 也是一个“短期支持版本”,该版本将在今年 7 月终止支持,而今年 4 月推出的 Ubuntu 24.04 LTS 长期支持版本将获得 5 年的更新支持。
|
8月前
|
Ubuntu 安全 小程序
linux|ubuntu.v18.10版本即将发布,linux桌面让您动心
如果你使用闭源系统,那永远也就别想了!有了这样的需求,也许最终将linux带到人类大众通用市场的是我们中国!
301 0
|
8月前
|
Web App开发 Ubuntu Linux
又该换Linux版本了!
如果你经常用谷歌搜索,使用终端输入命令,推荐你使用Fedora而不是Ubuntu。 如果你不是一个技术用户或程序员,仍推荐使用Ubuntu,还不动手去试试,别忘了将你的体验留在评论区哦~
|
9月前
|
Linux Docker Windows
windows docker安装报错适用于 Linux 的 Windows 子系统必须更新到最新版本才能继续。可通过运行 “wsl.exe --update” 进行更新。
适用于 Linux 的 Windows 子系统需更新至最新版本(如 wsl.2.4.11.0.x64.msi)以解决 2025 年 Windows 更新后可能出现的兼容性问题。用户可通过运行 “wsl.exe --update” 或访问提供的链接下载升级包进行更新。
3636 0
|
应用服务中间件 Linux nginx
【Azure App Service】基于Linux创建的App Service是否可以主动升级内置的Nginx版本呢?
基于Linux创建的App Service是否可以主动升级内置的Nginx版本呢?Web App Linux 默认使用的 Nginx 版本是由平台预定义的,无法更改这个版本。
356 77
|
11月前
|
消息中间件 NoSQL Linux
Redis的基本介绍和安装方式(包括Linux和Windows版本),以及常用命令的演示
Redis(Remote Dictionary Server)是一个高性能的开源键值存储数据库。它支持字符串、列表、散列、集合等多种数据类型,具有持久化、发布/订阅等高级功能。由于其出色的性能和广泛的使用场景,Redis在应用程序中常作为高速缓存、消息队列等用途。
1028 16
|
Ubuntu Linux
查看Linux系统架构的命令,查看linux系统是哪种架构:AMD、ARM、x86、x86_64、pcc 或 查看Ubuntu的版本号
查看Linux系统架构的命令,查看linux系统是哪种架构:AMD、ARM、x86、x86_64、pcc 或 查看Ubuntu的版本号
6741 4
下一篇
开通oss服务