【Linux】实现进度条小程序

慈云数据 2024-03-22 技术支持 58 0

个人主页 : zxctscl

如有转载请先通知

文章目录

  • 1. 前言
  • 2. 回车和换行
  • 3. 缓冲区
  • 4. 进度条
    • 4.1 倒计时设置
    • 4.2 进度条
      • 4.2.1 实现简单进度条
      • 4.2.2 进度条完善
      • 5. 附进度条代码
        • 5.1 Processbar.h
        • 5.2 Processbar.c
        • 5.3 Main.c
        • 5.4 Makefile

          1. 前言

          在之前已经了解了 【Linux】vim的使用和 【Linux】编译器-gcc/g++使用还有 【Linux】自动化构建工具-make/Makefile,有了这些工具,这次来实现一个进度条小程序。

          2. 回车和换行

          换行:是换到新的一行。

          回车:是回到最开始。

          举个例子:在格子里面写字的时候,第一行写完,要到第二行,此时到的第二行的结尾,这个叫换行。而从第二行结尾回到第二行开头是回车。

          也可以先回车到第一行的开始,再换行到第二行。

          在这里插入图片描述

          回车和换行是两个动作。

          在老式键盘上面就很明显,而现在的键盘为了省空间就不这样了:

          在这里插入图片描述

          平时在C语言上用到的“\n”叫回车换行,既回车,又换行。

          如果想让只回车就用到"\r"。

          有的代码用"\r\n"表示回车换行,如果在"\r"和"\n"同时存在时,"\n"只是换行。

          在老式打字机上面就会有下面这样的拨片,当打完一行之后,得手动把它往回拨,一拨纸就往上走(换行),写入位置就往左走,对应就是回车。

          在这里插入图片描述

          3. 缓冲区

          在代码里面把"\n"去掉:

          在这里插入图片描述

          在编译过程中就会发现,再休眠3秒的时候这个程序并没有输出,而是当程序结束的时候程序才输出。

          在这里插入图片描述

          加"\n"消息就直接显示出来,然后再sleep。

          不加"\n"这个字符串打印就没有显示出来,先sleep,再到程序退出的时候才显示这个字符串。

          在这里插入图片描述

          那么在不加"\n"那么printf和sleep哪个先运行呢?

          C语言在执行代码时候,默认从上往下执行,这个叫顺序结构。

          printf只是没有把数据显示器上显示出来,并不是没有执行。只是在sleep期间这个字符串没有显示出来而已。

          那么在sleep期间字符串在哪里呢?

          它被保存到缓冲区里面。它就是一块内存空间。

          当printf的时候,把字符串拷贝到缓冲区里面,然后定期把数据刷新到显示器上面,此时就能看到这个字符串了。

          不带"\n"的时候,字符串就在缓冲区里,当return 0时,程序结束时,一般要自动冲刷缓冲区。

          想要在程序结束之前就刷新缓冲区,有3种方式:

          1. "\n"直接就把缓冲区数据就刷出来了。包含"\n"之前的全部刷新出来。

          举个例子:

          在这里插入图片描述

          会发现先出来"\n"之前的,剩下的程序结束才出来:

          在这里插入图片描述

          2. 缓冲区满了进行刷新

          3. 强制刷新。

          ffush把特定文件流进行刷新:

          在这里插入图片描述

          在Linux下一切皆文件,可以把显示器当文件看。

          在C语言中程序在启动时默认会打开三个输入输出流:

          stdin对应的设备是键盘,stdout和stderr对应的是显示器。

          在这里插入图片描述

          在系统中不管是设备还是文件一律都是FILE。

          一般打印输出用到的是stdout,显示到显示器上。

          为什么会默认打开这输入输出流?

          方便用户进行输入和输出。一般使用计算机的都有输入和输出的需求,所以一般就默认打开输入输出流,就不用在写代码打开上面键盘和显示器这些设备了。

          在代码中使用一下fflush:

            1 #include
            2 #include
            3
            4 int main()
            5 {
            6   printf("Hello World,hello...");
            7   fflush(stdout);
            8   sleep(3);
            9   return 0;
           10 }
          

          在这里插入图片描述

          编译发现缓冲区内容就直接冲刷出来了:

          在这里插入图片描述

          在这里插入图片描述

          为什么会存在缓冲区?

          缓冲区提高了用户效率。以前的printf是往硬件上写的,现在直接写到内存了。从内存拷贝内存数据的效率,肯定比从硬件拷贝到内存的效率高。

          它刷新的次数越少,单次刷新的数据量越大,效率越高。

          按行刷新是方便用户阅读。

          4. 进度条

          4.1 倒计时设置

          假设将格子是光标的位置,一般在输入的时候就会是下面这样的:

          在这里插入图片描述

          但是如果想要实现光标在同一个位置,实现倒计时的感觉,就行下面这样:用8会覆盖这个9。

          在这里插入图片描述

          但是8会覆盖这个9后,光标会往后走,想要把在8的位置输出7,光标就得回到8的位置,7就把8覆盖,依此类推,就能实现一个动态的倒计时。

          就是在同一个位置不停的覆盖,就能实现动态效果。

          在这里插入图片描述

          用代码来实现一下倒计时的感觉:

          在这里插入图片描述

          但这个代码输出并不是我们所想要的输出:

          在这里插入图片描述

          ------------------------------------------------------------------------------------------

          来修改一下代码,加上"\r":

          在这里插入图片描述

          这里发现暂停一段时间,数据并没有显示出来:

          在这里插入图片描述

          "\r"相当于我们写了一个数回到最开始,就清了一个数,所以最后什么也不显示。

          ------------------------------------------------------------------------------------------

          那就直接加一个强制刷新缓冲区:

            1 #include
            2 #include
            3
            4 int main()
            5 {
            6   int count=9;
            7   while(count>=0)
            8   {
            9     printf("%d\r",count);
           10     fflush(stdout);
           11     count--;
           12     sleep(1);
           13   }
           14   return 0;
           15 }
          

          来看看效果:

          在这里插入图片描述

          ------------------------------------------------------------------------------------------

          既然能够覆盖一个字符,那么肯定也能覆盖一个字符串。

            1 #include
            2 #include
            3
            4 int main()
            5 {
            6   int count=9;
            7   while(count>=0)
            8   {
            9     printf("倒计时:%d\r",count);
           10     fflush(stdout);
           11     count--;
           12     sleep(1);
           13   }
           14   printf("\n");
           15   return 0;
           16 }
          

          来看看效果图:

          在这里插入图片描述

          在这里插入图片描述

          如果从10开始:

          在这里插入图片描述

          就会出现这样的效果:

          在这里插入图片描述

          数字10,实际在显示器上显示的是字符,它转化成1字符和0字符。当在打印的9时候字符长度变短了,0就没有办法覆盖,就会一直在。

          所以这里用%2d:

          在这里插入图片描述

          这时候就没有问题了:

          在这里插入图片描述

          4.2 进度条

          这里用多文件来实现,Processbar.h用来声明,Processbar.c用来实现方法,Main.c用来调用Processbar.c里面的方法,再使用Makefile来实现自动化调用。

          在这里插入图片描述

          先写出基本的框架逻辑:

          在这里插入图片描述

          在这里插入图片描述

          在编译的时候不用.h文件,因为在.c文件中已经包含.h是在当前目录下的,在编译时候会展开头文件的。

          在Makefile里面写:

            1 processbar:Main.c Processbar.c
            2   gcc -o $@ $^ 
            3 .PHONY:clean
            4 clean:
            5   rm -f processbar
          

          测试一下代码:

          在这里插入图片描述

          4.2.1 实现简单进度条

          先画出进度条的示例:

          进度条在增加的同时,当前的进度也在变化,光标也在不停的旋转。

          在这里插入图片描述

          这个进度条实现的时候,缓冲区的长度从0%到100%,但是还得考虑"\0",所以长度定义为101。

          把缓冲区清空就用到memset。

          在这里插入图片描述

          想要在缓冲区里面写特殊符号,就直接用define:#define Style '#'。

          用循环来实现动态进度条打印,直接打印相对应的字符串,和倒计时一样用fflush(stdout);来刷新缓冲区,随着时间的增加,进度条也在不断增加:

          在这里插入图片描述

          来看看效果:

          在这里插入图片描述

          发现这里打印时间太慢了。

          使用usleep,它休眠的时间比sleep的小,所以这里就用usleep来进行休眠。

          在这里插入图片描述

          然后将字符串输出改为左对齐,加上进度条对应的比率:

          在这里插入图片描述

          来看看效果:

          在这里插入图片描述

          4.2.2 进度条完善

          但是一般进度条不会单独出现,只有当我们下载一个文件同时出现。

          这里就先模拟下载过程,就在在Main.c写一个download来实现。一般在下载一个文件的时候,会有下载文件大小和下载到多少,和网络的带宽。

          分别用变量定义一下文件大小,当前下载进度和带宽:

          double filesize = 1024*1024*1024*1.0;
          double current = 0.0; 
          double bandwidth = 1024*1024*1.0;
          

          还可以加上一个开始下载的提示:

          printf("download begin,current: %lf\n",current);
          

          和下载完成的提示:

          printf("\ndownload done,filesize: %lf\n",filesize);
          

          在下载过程中把要下载的文件大小,和目前已经下载的大小传给进度条ProcBar,让进度条时时交互。

          在这里插入图片描述

          在Processbar.h中记得把在Processbar.c使用的打印进度条函数声明一下:

          void ProcBar(double total,double current);
          

          在这里插入图片描述

          在Processbar.c中,就和上面的简单实现进度条一样,把文件的长度,和目前下载的长度传进去:

          void ProcBar(double total,double current)
          

          这里加了当前下载的进度百分比

          double rate=(current*100.0)/total;

          在循环的时候得将前下载的进度输出,循环的时候得把double rate的类型转为int:

          int loop_count=(int)rate;
          

          最后打印出当前下载的进度,还得冲刷缓冲区:

            printf("[%-100s][%.1lf%%][%c]\r",bar,rate,lable[cnt%len]);
            fflush(stdout);
          

          来看看效果

          在这里插入图片描述

          在这里插入图片描述

          在Processbar.h中定义一个函数指针类型:

          typedef void(*callback_t)(double,double);
          

          然后在Main.c里面将download改一下,可以根据文件的大小来下载:

          void download(double filesize,callback_t cb)
          

          在download函数里面直接执行:cb(filesize,current);方法。

          为什么要这么写?

          在之后如果用户写一个图形化界面的进度条,就能在下载的时候同时更新图形化界面。

          在这里插入图片描述

          也可以让进度条加上颜色,只需要在网上找一个关于用C语言输出有颜色相关的内容也就会有了。

          这里是加了颜色效果:

           printf("\033[40;44m[%-100s][%.1lf%%][%c]\r\033[0m",bar,rate,lable[cnt%len]);
          

          在这里插入图片描述

          在这里插入图片描述

          5. 附进度条代码

          5.1 Processbar.h

            1 #pragma once
            2
            3 #include
            4
            5 typedef void(*callback_t)(double,double);
            6 void ProcBar(double total,double current);
            7 //extern void ForTest();
          

          5.2 Processbar.c

            1 #include "Processbar.h"
            2 #include 
            3 #include 
            4
            5 #define Length 101
            6 #define Style '#'
            7
            8 const char *lable = "|/-\\";
            9
           10 
           11 void ProcBar(double total,double current)
           12 {
           13   char bar[Length];
           14   memset(bar,'\0',sizeof(bar));
           15   int len=strlen(lable);
           16 
           17   int cnt = 0;
           18   double rate=(current*100.0)/total;
           19   int loop_count=(int)rate;
           20   while(cnt
           22     bar[cnt++]=Style;
           23 
           24   }
           25 
           26    //没有加颜色前的输出 printf("[%-100s][%.1lf%%][%c]\r",bar,rate,lable[cnt%len]);
                  printf("\033[40;44m[%-100s][%.1lf%%][%c]\r\033[0m",bar,rate,lable[cnt%len]);
           27     fflush(stdout);
           28 }
          
            7   double current = 0.0;
            8   
            9
           10   printf("download begin,current: %lf\n",current);
           11   while(current
           13     cb(filesize,current);
           14     usleep(100000);
           15     current+=bandwidth;
           16
           17   }
           18   printf("\ndownload done,filesize: %lf\n",filesize);
           19 }
           20
           21
           22 int main()
           23 {
           24   download(101*1024*1024,ProcBar);
           25  // ProcBar(100.0,56.9);
           26
           27 // ProcBar(100.0,1.0);
           28  // ProcBar(100.0,99.9);
           29
           30  // ProcBar(100.0,100);
           31   //ForTest();
           32   return 0;
           33 }
          ~
          
微信扫一扫加客服

微信扫一扫加客服

点击启动AI问答
Draggable Icon