耶耶耶耶耶 发表于 2024-8-6 15:52:03

【Linux】编写第一个小步伐:进度条

https://i-blog.csdnimg.cn/blog_migrate/a37d2a42b74218f773c3c9bbcb486984.gif
1. 准备知识

1.1 简单认识几个函数

1.1.1 sleep()

unsigned int sleep(unsigned seconds);
   

[*]作用:让步伐休眠指定秒数,如:sleep(3); //让步伐休眠3秒
[*]与 Windows 上的 Sleep() 函数差异
[*]必要包含头文件<unistd.h>
1.1.2 fflush()

int fflush(FILE* stream);
   

[*]作用:刷新缓冲区
[*]必要传入一个流
[*]必要包含头文件<stdio.h>
1.1.3 usleep()

int usleep(useconds_t usec);
   

[*]作用:让步伐休眠指定微秒,如:usleep(100000); //让步伐休眠100000微秒(0.1秒)
[*]1秒 = 1000000微秒
[*]必要包含头文件<unistd.h>
1.1.4 memset()

void* memset(void* ptr, int value, size_t num);
   

[*]作用:将 ptr 指向的内存块的前 num 个字节设置为指定的 value 值
[*]返回设置后的 ptr
[*]必要包含头文件<string.h>
1.2 缓冲区

直接上代码观察现象
#include <stdio.h>
#include <unistd.h>

int main()
{
    printf("you can see me       ");
    sleep(3);
    return 0;
}
执行结果图
https://i-blog.csdnimg.cn/blog_migrate/b02eb02ee2125283e2f3758d66da3a21.png
   

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

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

[*]回车( \r ):把光标放到当前行的开始。
https://i-blog.csdnimg.cn/blog_migrate/0f538b2d0f57bb23042a9fbf7c2375b5.png
   

[*]换行( \n ):把光标放到当前位置的下一行。
https://i-blog.csdnimg.cn/blog_migrate/24878a89fe703f1dda503e5ff12d4590.png
   

[*]以是理论上来讲,‘\n’ 和 ‘\r’ 一起用才是我们明确中的”回车“,即:把光标放到下一行最开始的位置。
2. 编写入门版的进度条

2.1 根本逻辑

   

[*]进度 1% 打印 1 个字符,回车到开始的位置,刷新缓冲区;
[*]进度 2% 打印 2 个字符,回车到开始的位置,刷新缓冲区;
[*]…
[*]进度 100% 打印 100 个字符,回车到开始的位置,刷新缓冲区,步伐停止。
https://i-blog.csdnimg.cn/blog_migrate/9a0e18c6390499d7258dc1a818e3d6ae.png
2.2 美化结果

   

[*]进度条主体增长箭头显示
[*]显示进度百分比
[*]添加一个动态的旋转光标
https://i-blog.csdnimg.cn/blog_migrate/e2066739c58ccb846434312990a481d5.png
2.3 代码实现

// porcessbar.h

#pragma once

#include <stdio.h>

#define NUM 103
#define Body '='
#define Head '>'

// version 1
void process();
// processbar.c

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

const char* lable = "|/-\\";

// version 1
void process()
{
    char buffer;
    memset(buffer, '\0', sizeof(buffer));
    int cnt = 0;
    int n = strlen(lable);
    buffer = Head;
    while (cnt <= 100)
    {
      printf("[%-100s][%3d%%][%c]\r", buffer, cnt, lable);
      fflush(stdout);
      buffer = Body;
      if (cnt < 100)
      {
            buffer = Head;
      }
      usleep(50000);
    }
    printf("\n");
}
2.4 执行结果

https://i-blog.csdnimg.cn/blog_migrate/162081107287542a55ff009ef65e38de.png
https://i-blog.csdnimg.cn/blog_migrate/14e9d08d553a2350e8f9f384fd0ecba5.png
https://i-blog.csdnimg.cn/blog_migrate/e76973e2d77f8b11765ce479894a3966.png
3. 编写升级版的进度条

   

[*]上面的进度条算是一个进度条吗?我们的进度条似乎在一个人玩单机呀,这样的进度条是没有意义的。
[*]进度条的进度应该是依靠于其他应用的,比如下载。
[*]下面我们模仿一个下载情况,并修改进度条,使进度条可以根据下载的进度,同步举行显示进度的工作。
3.1 代码实现

// processbar.h

#pragma once

#include <stdio.h>

#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 <string.h>
#include <unistd.h>

const char* lable = "|/-\\";

// version 2: 进度是多少,你进度条能知道吗?另外,什么进度?依附于其他应用的,比如下载
char buffer = {0};
void process_flush(double rate)
{
    static int cnt = 0;
    int n = strlen(lable);
    if (rate <= 1.0)
    {
      buffer = Head;
    }
    printf("[%-100s][%.1f%%][%c]\r", buffer, rate, lable);
    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 <time.h>
#include <stdlib.h>
#include <unistd.h>

// 模拟文件大小
#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 执行结果

https://i-blog.csdnimg.cn/blog_migrate/17c31d10a823006a152dd557293ffd11.png
https://i-blog.csdnimg.cn/blog_migrate/9410fd3a35f18fa211b4b7b139f76d14.png
https://i-blog.csdnimg.cn/blog_migrate/d76ffbf4a85198592a8e48d863aa34c1.png
   END
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: 【Linux】编写第一个小步伐:进度条