• Linux从0到1——Linux第一个小程序:进度条


    1. 输出缓冲区


    1. 小实验:

    编写一个test.c文件,:

    #include 
    #include 
    
    int main()
    {
        printf("你能看见我吗?\n");
        sleep(1);   // 暂停1秒
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    编译并执行:

    在这里插入图片描述

    先打印,然后暂停一秒结束程序,很好理解。

    2. 发现问题:

    修改test.c文件内容如下:

    在这里插入图片描述

    再次编译并执行:

    请添加图片描述

    发现运行可执行程序后,没有直接打印内容,而是隔了一秒钟,才打印,这好像和我们理解的不太一样,是怎么回事?

    我们可以确定的是,一定是printf先执行的,因为C语言代码一定是从上到下运行的,但是现象是字符没有打印。所以,我们可以断定,printf其实早就运行了,只不过在sleep期间,字符串没有被显示出来。在sleep期间,字符串在输出缓冲区当中。

    3. 缓冲区:

    C/C++语言,会针对标准输出,给我们提供默认的缓冲区(stdout)。我们可以使用fflush函数,可以把一个流强制做刷新(标准输入输出流之后会讲,这里只需要知道它可以刷新输出缓冲区)。

    验证:

    在这里插入图片描述

    编译并执行:

    请添加图片描述

    那为什么加\n的程序,不需要刷新缓冲区?这是因为\n是一种刷新策略,叫行刷新,默认就有刷新缓冲区的功能。


    2. 回车和换行的本质


    在这里插入图片描述

    我们来写一个倒计时程序:

    #include 
    #include 
    
    int main()
    {
        int cnt = 9;
        while(cnt)
        {
            printf("%d\n", cnt);
            cnt--;
            sleep(1);
        }
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    运行一下:

    请添加图片描述

    可以发现它是换行打印倒计时,但是我们想让它只在一行打印,并且覆盖掉前一秒的秒数,如何做?\n是换行加回车,我们现在的需求是只回车,不换行,可以通过\r实现,但是\r没有刷新缓冲区的功能。

    修改如下:

    在这里插入图片描述

    运行一下:

    请添加图片描述


    3. 实现进度条


    3.1 简单原理版本


    1. 原理讲解:

    我们期望的进度条形式如下:

    请添加图片描述

    进度条的风格是#,右侧有一个百分数,提示当前具体进度,还有一个旋转光标,可以确定当进度条不动时,进程是还在进行还是卡住了。

    • 进度条的实现:第一次输出#,第二次输出##,一次类推,#越来越多。为了实现图中结果,我们可以通过不断回车,然后覆盖之前的#实现,比如用##覆盖#
    • 旋转光标的实现:利用一个常量字符串实现,让字符在|/-\这几个字符按顺序不断转换,实现旋转效果。

    2. 文件准备:

    在这里插入图片描述

    main.c文件是主函数所在文件,进度条的具体实现在process.c文件中,主函数文件通过头文件process.h调用进度条的实现函数,各个文件内容如下(最重要是process.c)。

    • process.c文件:
    #include "process.h"                                                                                        
    #include    // sleep的头文件
    #include 
    
    #define SIZE 101
    #define MAX_RATE 100
    #define STYLE '#'
    #define STIME 1000*40
       
    const char* str="|/-\\"; // 旋转光标
    
    void process()
    {
        // version 1
        int rate = 0;
        char bar[SIZE] = {0}; // 全部初始化成'\0'
        int num = strlen(str);
    
        while(rate <= MAX_RATE)
        {
            printf("[%-100s][%d%%][%c]\r", bar, rate, str[rate%num]); // 打印百分号最好用%%
            fflush(stdout);
            usleep(STIME); // 单位是u秒
            bar[rate++] = STYLE;
        }
        printf("\n");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • main.c文件:
    #include "process.h"                                                   
     
    int main()
    {
        process();
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • process.h文件:
    #pragma once
     
    #include  
    
    void process();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 编辑Makefile

    在这里插入图片描述


    3.2 实际工程版本


    无论是任何进度条,一定是和某种任务关联的!

    • process.c文件:
    #include "process.h"
    
    const char* str="|/-\\"; // 旋转光标
    
    void process_v1()
    {
        // version 1
        int rate = 0;
        char bar[SIZE] = {0}; // 全部初始化成'\0'
        int num = strlen(str);
        
        while(rate <= MAX_RATE)
        {
            printf("[%-100s][%d%%][%c]\r", bar, rate, str[rate%num]); // 打印百分号最好用%%
            fflush(stdout);
            usleep(STIME); // 单位是u秒
            bar[rate++] = STYLE;
        }
        printf("\n");
    }
    
    // 不能一次将进度条打印完毕,否则无法平滑的和场景结合
    // 该函数,应该根据rate,自动的打一次
    void process_v2(int rate)
    {   
        // version 2
        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));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • process.h文件:
    #pragma once
    
    #include 
    #include 
    #include 
    
    #define SIZE 101
    #define MAX_RATE 100
    #define STYLE '#'
    #define STIME 1000*40
    
    // 定义了一个函数指针类型,其中函数的参数是int,返回值是void
    typedef void callback_t(int);
    
    void process_v1();
    void process_v2(int);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • main.c文件:
    #include "process.h"
    
    #define TARGET_SIZE 1024*1024 // 1MB,下载软件总大小
    #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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    我们希望进度条在进度条函数内部循环打印,所以我们采用回调的方式,来进行某种任务的通知,动态更新进度条!


  • 相关阅读:
    freeswitch 使用 silero-vad 静音拆分使用 fastasr 识别
    无需debug,通过抽象模型快速梳理代码核心流程
    Go语言内置类型和函数
    推荐系统实践读书笔记-05利用上下文信息
    Code Former安装及使用
    Python自学笔记——高级篇(面向对象)
    node.js 简介 以及 模块化 的介绍
    rsync 远程同步
    React学习之路 - HTML5 新特性
    C# .Net MAUI 从入门到入土
  • 原文地址:https://blog.csdn.net/weixin_73870552/article/details/136676418