• Linux 编写一个 简单进度条


    进度条

    回车换行理解:

    我们要理解,回车换行是两个概念:

    • 换行是把光标移到下一行,是竖直的往下平移;" \n "
    • 回车是把光标移到当前行的最开始;                  " \r " 

     就和一起打字机一样:

     在打字的时候,有一个 负责打印印刷在纸上的机器,是跟随者使用人打印文字,往有迭代走的,当这个机器走到 最右边的时候,或者使用人想要再次换行的时候,使用人机会把这个机器从最右边 手动 移动到 最左边,这个过程就和上述的 "r " 换行一样。

    而且在换行的时候,纸还会往上走,这个过程就相当于是 回车了。

    还有老式键盘的回车键,是 一个 从上往下,从左往右的造型:

     这也是模拟了 此处的回车键 是代表的是 回车换行的作用。

     缓冲区理解

     首先我们来看一个简单的代码:

     我们在 Linux 下运行这个代码的话,会先打印 hello C! 然后 程序休眠两秒 才会出来下一个 word:

     但是这是我们在printf 当中输出了 \n 的情况;如果不输出  \n 的话 又会是什么情况呢?

    程序源码展示:

     输出:

     发现好像不对劲啊,C语言一定是从 上往下执行的,那么应该是 先输出 hello C! 然后在 休眠啊。

    但是看上述的输出结果,好像是 休眠在输出啊?

    所以,上述printf()已经打印完毕了,只不过还没有显示出来,那么在 sleep()的两秒内,这个 "hello C!" 字符串是在哪里存储的呢?

    其实就是存储在 缓冲区当中的,因为 上述使用 C语言编写代码,所以此时,这个缓冲区是由 C语言管理的一块缓冲区。

    当程序 sleep 结束了在缓冲区当中的数据才会被刷出来。

    C程序默然会为我们打开三个 输入输出流:标准输入,标准输出(显示器,输出类型为 stdout),标准错误。

    stdout 其实就是 FILE* 类型;

     我们可以使用 stdio.h 当中的 fflush(FILE* stream)函数,强制把 缓冲区当中的数据给刷出来。

     输出:

     至此,我们就可以简单的实现一个倒计时了:

     上述的 "-" 表示每一次都从 最左边开头输出;"2" 表示,不管当前输出什么,都按照两个字符输出;"\r" 表示每一次都当前行的最左边输出。

    如果不加 "\r" 将会是 一秒输出一个数字 输出: 109876543210 。

    进度条实现

    那么在linux 当中,实现一个简单的进度条就和上述倒计时小程序是类似的,我们只需要把 每一次 光标移动到最前面的时候输出的字符,从 1 个,改为依次增加一个字符输出就行了。

    利用就是 linux 当中不使用 "/n" 等等类似操作的话,就不会立即刷新缓冲区当中的内容,我们把这种刷新方式称之为 行刷新。比如:使用 "\r" 回车的话,就不会立即刷新 缓冲区当中的内容,也就不会立即打印出来。

    我们可以使用 fflush(stdout)的方式来强制输出 缓冲区当中的内容。

    我们先把 中间填充的部分完善,按照上述倒计时的方式修改。

     上述printf()函数当中的输出,“100” 是为了保证 在 “[]”当中一直有最大 100 个字符的 “\0” 填充,"-" 代表从当前行最左边开始输出(因为 当使用上述 的填充之后,C语言默认从最最右边开始输出), "%%" 是输出一个 "%",因为 "%" 这个字符在printf ()当中是格式化输出的字符,所以只能直接用,当然 可以 "/%" 使用转移字符。

    上述输出:

     但是,上述的进度条有点太单一了,我们还可以加上一些旋转图标等等,我们就简单实现,覆盖式打印 "|"  "/"  "--" "\"  "|"  ,简单实现。

    完整代码:

    1. 1 #include
    2. 2 #include
    3. 3 #include
    4. 4
    5. 5 #define NUM 100
    6. 6 #define STYLE '='
    7. 7 #define RIGHT '>'
    8. 8 #define TOP 100
    9. 9 #define SPEED 10000
    10. 10
    11. 11 const char* labar = "|/-\\";
    12. 12
    13. 13 void processbar(int speed)
    14. 14 {
    15. 15 int cn = 0;
    16. 16 char array[NUM];
    17. 17
    18. 18 memset(array, '\0', sizeof(array));
    19. 19 int len = strlen(labar);
    20. 20
    21. 21 while(cn
    22. 22 {
    23. 23 printf("[%-100s][%d%%][%c]\r",array, cn + 1 ,labar[cn%len]);
    24. 24 fflush(stdout);
    25. 25 array[cn++] = STYLE;
    26. 26
    27. 27 if(cn < TOP) array[cn] = RIGHT;
    28. 28 usleep(SPEED);
    29. 29 }
    30. 30 printf("\n");
    31. 31 }
    32. 32
    33. 33 int main()
    34. 34 {
    35. 35 processbar(10000);
    36. 36
    37. 37 return 0;
    38. 38 }

    上述是进度条自己在运行,但是我们在使用进度条的时候,一般都是记录一下外部程序运行的进度的,那么进度条函数本身是不知道的,只有外部程序调用者才知道当前 程序的进度,所以我们还需要对上述进度条进行改进:

    1. #include
    2. #include
    3. #include
    4. #define NUM 100
    5. #define STYLE '='
    6. #define RIGHT '>'
    7. #define TOP 100
    8. const char* labar = "|/-\\";
    9. char array[NUM] = { 0 };
    10. void processbar(int rate)
    11. {
    12. if (rate < 0 || rate > 100) return;
    13. 17 int len = strlen(labar);
    14. printf("[%-100s][%d%%][%c]\r", array, rate, labar[rate % len]);
    15. fflush(stdout);
    16. array[rate++] = STYLE;
    17. if (rate < 100) array[rate] = RIGHT;
    18. }
    19. int main()
    20. {
    21. int total = 1000; // 比如现在要下载一个 1000MB的文件
    22. int curr = 0;
    23. //因为进度条是在外部使用进度条的人才知道当前程序的进度
    24. //所以 循环不能写在 进度条函数当中
    25. //应该写在外部
    26. while (curr <= total)
    27. {
    28. //这里就模拟外部程序运行了
    29. processbar(curr * 100 / total);
    30. curr += 10;
    31. usleep(50000);
    32. }
    33. printf("\n");
    34. return 0;
    35. }

    此时,在进度条函数当中,就只有 100 打印当中的一次答打印,我们把 循环做到了外层 程序当中。

    其实还可以更简化,可以使用 函数指针类型 来实现更简单的进度条调用

    1. typedef void (*callback_t)(int); // 函数指针类型
    2. // 模拟一种下载
    3. void download(callback_t cb)
    4. {
    5. int total = 1000;
    6. int curr = 0;
    7. while (curr <= total)
    8. {
    9. // 此时正在下载
    10. usleep(50000); // 模拟下载时间
    11. int rate = curr * 100 / total;
    12. cb(rate);// 通过函数指针的回调,调用 processbar函数
    13. curr += 10;
    14. }
    15. printf("\n");
    16. }
    1. void processbar(int rate)
    2. {
    3. if (rate < 0 || rate > 100) return;
    4. int len = strlen(labar);
    5. printf("[%-100s][%d%%][%c]\r", array, rate, labar[rate % len]);
    6. fflush(stdout);
    7. array[rate++] = STYLE;
    8. if (rate < 100)
    9. {
    10. array[rate] = RIGHT;
    11. }
    12. else
    13. {
    14. initarray();
    15. }
    16. }

    如上所示,我们模拟实现一个 下载软件的 过程,把 进度条函数通过函数指针的方式传入到 download当中,在 download()函数当中,通过函数指针调用到 进度条函数,就可以简便的调用进度条函数。

     所以,在主函数当中,我们只需要调用 download()函数,就行:

    1. int main()
    2. {
    3. download(processbar);
    4. return 0;
    5. }

     上述就是 简单的进度条实现,当然,你还可以用 C当中一些颜色输出来美化 进度条:
    c语言中如何更改输出字符的颜色 - CSDN文库

  • 相关阅读:
    expo + react native项目隐藏状态栏踩坑
    C#中的类型转换-自定义隐式转换和显式转换
    APP订单管理软件生活中不可或缺|小程序系统定制开发|网站搭建
    编译器优化记录(死代码消除+“激进的”死代码消除)
    在VMD上可视化hdf5格式的分子轨迹文件
    OpenCV-Python学习(15)—— OpenCV 图像旋转角度计算(NumPy 三角函数)
    算法——多数相和
    1800_vim的宏录制功能尝试
    zookeeper节点类型
    Python pyintsaller打包异常 type object ‘Callable‘ has no attribute ‘_abc_registry‘
  • 原文地址:https://blog.csdn.net/chihiro1122/article/details/133797547