【Linux】编写第一个小程序:进度条
作者:mmseoamin日期:2024-02-03

文章目录

  • 1. 预备知识
    • 1.1 简单认识几个函数
      • 1.1.1 sleep()
      • 1.1.2 fflush()
      • 1.1.3 usleep()
      • 1.1.4 memset()
      • 1.2 缓冲区
      • 1.3 回车与换行
      • 2. 编写入门版的进度条
        • 2.1 基本逻辑
        • 2.2 美化效果
        • 2.3 代码实现
        • 2.4 执行效果
        • 3. 编写升级版的进度条
          • 3.1 代码实现
          • 3.2 执行效果

            【Linux】编写第一个小程序:进度条,在这里插入图片描述,第1张

            1. 预备知识

            1.1 简单认识几个函数

            1.1.1 sleep()

            unsigned int sleep(unsigned seconds);
            
            • 作用:让程序休眠指定秒数,如:sleep(3); //让程序休眠3秒
            • 与 Windows 上的 Sleep() 函数不同
            • 需要包含头文件

            1.1.2 fflush()

            int fflush(FILE* stream);
            
            • 作用:刷新缓冲区
            • 需要传入一个流
            • 需要包含头文件

            1.1.3 usleep()

            int usleep(useconds_t usec);
            
            • 作用:让程序休眠指定微秒,如:usleep(100000); //让程序休眠100000微秒(0.1秒)
            • 1秒 = 1000000微秒
            • 需要包含头文件

            1.1.4 memset()

            void* memset(void* ptr, int value, size_t num);
            
            • 作用:将 ptr 指向的内存块的前 num 个字节设置为指定的 value 值
            • 返回设置后的 ptr
            • 需要包含头文件

            1.2 缓冲区

            直接上代码观察现象

            #include 
            #include 
            int main()
            {
                printf("you can see me       ");
                sleep(3);
                return 0;
            }
            

            执行效果图

            【Linux】编写第一个小程序:进度条,在这里插入图片描述,第2张

            • 首先要否定上面不切实际的想法,C语言是顺序执行的,所以 printf 函数一定先于 sleep 函数执行。
            • 那为什么 3 秒后才打印到屏幕上呢?当然是因为缓冲区!
            • printf 函数跑完后,输出的字符串是被保存到 C 对 IO 函数提供的一个缓冲区里了,在程序退出的时候,缓冲区中的内容才被刷新到屏幕上。
            • 我们需要使用上面讲的 fflush 函数把缓冲区中的内容提前强制刷新到屏幕上,使用方法:fflush(stdout);

            1.3 回车与换行

            首先我要抛出一个概念:回车和换行是不一样的!

            • 回车( \r ):把光标放到当前行的开始。

            【Linux】编写第一个小程序:进度条,在这里插入图片描述,第3张

            • 换行( \n ):把光标放到当前位置的下一行。

            【Linux】编写第一个小程序:进度条,在这里插入图片描述,第4张

            • 所以理论上来讲,‘\n’ 和 ‘\r’ 一起用才是我们理解中的”回车“,即:把光标放到下一行最开始的位置。

            2. 编写入门版的进度条

            2.1 基本逻辑

            • 进度 1% 打印 1 个字符,回车到开始的位置,刷新缓冲区;
            • 进度 2% 打印 2 个字符,回车到开始的位置,刷新缓冲区;
            • 进度 100% 打印 100 个字符,回车到开始的位置,刷新缓冲区,程序终止。

            【Linux】编写第一个小程序:进度条,在这里插入图片描述,第5张

            2.2 美化效果

            • 进度条主体增加箭头显示
            • 显示进度百分比
            • 添加一个动态的旋转光标

            【Linux】编写第一个小程序:进度条,在这里插入图片描述,第6张

            2.3 代码实现

            // porcessbar.h
            #pragma once
            #include 
            #define NUM 103
            #define Body '='
            #define Head '>'
            // version 1
            void process();
            
            // processbar.c
            #include "processbar.h"
            #include 
            #include 
            const char* lable = "|/-\\";
            // version 1
            void process()
            {
                char buffer[NUM];
                memset(buffer, '\0', sizeof(buffer));
                int cnt = 0;
                int n = strlen(lable);
                buffer[0] = Head;
                while (cnt <= 100)
                {
                    printf("[%-100s][%3d%%][%c]\r", buffer, cnt, lable[cnt % n]);
                    fflush(stdout);
                    buffer[cnt++] = Body;
                    if (cnt < 100)
                    {
                        buffer[cnt] = Head;
                    }
                    usleep(50000);
                }
                printf("\n");
            }
            

            2.4 执行效果

            【Linux】编写第一个小程序:进度条,在这里插入图片描述,第7张

            【Linux】编写第一个小程序:进度条,在这里插入图片描述,第8张

            【Linux】编写第一个小程序:进度条,在这里插入图片描述,第9张

            3. 编写升级版的进度条

            • 上面的进度条算是一个进度条吗?我们的进度条似乎在一个人玩单机呀,这样的进度条是没有意义的。
            • 进度条的进度应该是依赖于其他应用的,比如下载。
            • 下面我们模拟一个下载环境,并修改进度条,使进度条可以根据下载的进度,同步进行显示进度的工作。

            3.1 代码实现

            // processbar.h
            #pragma once
            #include 
            #define NUM 103
            #define Body '='
            #define Head '>'
            typedef void (*callback_t)(double);		// 函数指针类型
            // version 2
            void process_flush(double rate);
            
            // processbar.c
            #include "processbar.h"
            #include 
            #include 
            const char* lable = "|/-\\";
            // version 2: 进度是多少,你进度条能知道吗?另外,什么进度?依附于其他应用的,比如下载
            char buffer[NUM] = {0};
            void process_flush(double rate)
            {
                static int cnt = 0;
                int n = strlen(lable);
                if (rate <= 1.0)
                {
                    buffer[0] = Head;
                }
                printf("[%-100s][%.1f%%][%c]\r", buffer, rate, lable[cnt % n]);
                fflush(stdout);
                buffer[(int)rate] = Body;
                if ((int)rate + 1 < 100)
                {
                    buffer[(int)(rate + 1)] = Head;
                }
                if (rate >= 100.0)
                {
                    printf("\n");
                }
                cnt++;
                cnt %= n;
            }
            
            // main.c
            #include "processbar.h"
            #include 
            #include 
            #include 
            // 模拟文件大小
            #define FILESIZE (1024 * 1024 * 1024)
            // 模拟一种场景,表示一种下载任务
            void download(callback_t cb)    // 回调函数的形式
            {
                srand(time(NULL) ^ 1023);   // 这样写只是为了让随机数更随机
                int total = FILESIZE;
                while (total)
                {
                    usleep(10000);  // 下载动作
                    int one = rand() % (1024 * 1024);  // 一次下载的大小
                    total -= one;
                    if (total < 0)
                    {
                        total = 0;
                    }
                    // 当前的进度是多少?
                    int download = FILESIZE - total;
                    double rate = (download * 1.0 / FILESIZE) * 100.0;
                    cb(rate);
                }
            }
            int main()
            {
                download(process_flush);
                return 0;
            }
            

            3.2 执行效果

            【Linux】编写第一个小程序:进度条,在这里插入图片描述,第10张

            【Linux】编写第一个小程序:进度条,在这里插入图片描述,第11张

            【Linux】编写第一个小程序:进度条,在这里插入图片描述,第12张


            END