Linux 编程入门:打造你的首个动态进度条

目录

1、补充:回车与换行

2、行缓冲区

[(1) make执行下述代码会出现何现象?](#(1) make执行下述代码会出现何现象?)

[(2) make执行下述代码会出现何现象?](#(2) make执行下述代码会出现何现象?)

[(3) 如何让不带【\n】的字符串自动行刷新?](#(3) 如何让不带【\n】的字符串自动行刷新?)

3、练手:倒计时代码

4、进度条代码

[(1) process.c](#(1) process.c)

[(2) process.h](#(2) process.h)

[(3) main.c](#(3) main.c)

[(4) 效果演示](#(4) 效果演示)


进度条动态效果演示

进度条

1、补充:回车与换行

(1) 回车:

解释为将光标移至行首,转义表达为【\r】。

(2) 换行:

解释为将光标移至下一行,转义表达为【\n】。

(3) 老式打字机:

在早期的打字机上,回车操作是指将打印机的打印头(Carriage)复位到行的起始位置,以便开始新的一行;换行操作是指使滚筒上卷,带动纸张向上移动一行的距离,以开启新的一行。

2、行缓冲区

前提:makefile里是下述内容,会方便执行。

bash 复制代码
$ vim Makefile
  1 SRC=$(wildcard *.c)
  2 OBJ=$(SRC:.c=.o)
  3 BIN=processbar
  4 
  5 $(BIN):$(OBJ)
  6     gcc -o $@ $^
  7 
  8 %.o:%.c
  9     gcc -c $<
 10 
 11 .PHONY:clean
 12 clean:
 13     rm -f $(OBJ) $(BIN)

(1) make执行下述代码会出现何现象?

cpp 复制代码
$ vim myproc.c
 1 #include <stdio.h>
 2 int main()
 3 {
 4     printf("hello Linux!\n");
 5     sleep(3);
 6     return 0;
 7 }

现象:我们会观察到显示屏先出现"hello Linux",再停留三秒后才启动下一命令行。

解释:【\n】可以行刷新,程序走到\n会自动刷新在缓冲区的这一行字符,所以先显示字符串了。

(2) make执行下述代码会出现何现象?

cpp 复制代码
$ vim myproc.c   
 1 #include <stdio.h>
 2 int main()
 3 {
 4     printf("hello Linux!");
 5     sleep(3);
 6     return 0;
 7 }

现象:显示器上三秒后才出现字符串,新的命令行也紧跟字符串后面。为什么printf明明在sleep前面,却在休眠后显示?

解释 :首先要明确的是,在程序内printf是比sleep先执行的 ,但是显示器没有显示是因为,【\r】没有行刷新功能,在休眠期间,"hello Linux"一直在缓冲区,没有被刷新 ,最后又被显示出来是因为程序退出会自动刷新一次缓冲区。

(3) 如何让不带【\n】的字符串自动行刷新?

众所周知,**运行程序时,系统会默认打开三个文件:stdin,stdout,stderr,分别对应的设备文件是键盘,显示器,显示器。**而我们使用的printf会把字符串写到标准输出stdout中,但由于没有【\n】,字符串就留在缓冲区。

要解决该问题,我们介绍一个函数:fflush是 C 语言标准库中的一个函数,用于控制输出流。它定义在 <stdio.h> 头文件中,其作用是将指定的输出流(通常是文件流)中的缓冲区内容强制写入到文件或设备中。这个函数可以用来确保缓冲区中的数据被立即输出,而不是等到缓冲区满了或者程序结束时。

cpp 复制代码
$ vim myproc.c
 1 #include <stdio.h>
 2 int main()
 3 {
 4     printf("hello Linux!");
 5     fflush(stdout);
 6     sleep(3);
 7     return 0;
 8 }

执行上述代码,我们发现与第一组代码执行现象一致。

3、练手:倒计时代码

cpp 复制代码
  1 #include <stdio.h>
  2 #include<unistd.h>
  3 int main()
  4 {
  5     int num = 10;
  6     while(num >= 0)                                                                                                       
  7     {
  8         printf("%-2d\r",num); // 左对齐,位宽为2
  9         num--;
 10         fflush(stdout); // [\r]不能行刷新,要用到fflush
 11         sleep(1);
 12     }
 13     printf("\n");
 14     return 0;
 15 }

4、进度条代码

(1) process.c

cpp 复制代码
$ vim process.c
  1 #include<stdio.h>
  2 #include<string.h>
  3 #include<unistd.h>
  4 
  5 #define NUM 101
  6 #define STYLE '='
  7 
  8 void process_v1()
  9 {
 10     char buffer[NUM];
 11     memset(buffer,0,sizeof(buffer)); // buffer数组里全设置成0
 12     const char* table = "|/-\\"; // [\\]表示[\]
 13     int len = strlen(table);
 14 
 15     int cnt = 0;
 16     while(cnt <= 100)
 17     {
 18         printf("[%-100s][%d%%][%c]\r",buffer,cnt,table[cnt%len]);
 19         fflush(stdout);
 20         buffer[cnt] = STYLE;
 21         cnt++;
 22         usleep(50000); // 以微秒为单位休眠
 23     }
 24     printf("\n");
 25 }

这样的进度条已经初具模型了,但是我们使用进度条一般是一般加载资源一边刷新进度条,而不是单纯的自己循环,接下来我们实现第二个版本。版本2的不用自主实现循环而是与资源下载相关联,资源下载实现在了main函数所在的文件。

cpp 复制代码
 27 void FlushProcess(double total, double current)
 28 {
 29     char buffer[NUM];
 30     memset(buffer,0,sizeof(buffer));
 31     const char* table = "|/-\\";
 32     int len = strlen(table);
 33 
 34     static int cnt = 0; // 全局静态
 35     int num =(int)(current*100/total); // 确定填充的大小范围
 36     for(int i = 0;i < num;i++) // 填充"=",不用自己循环
 37     {
 38         buffer[i] = STYLE;
 39     }
 40     // 比例
 41     double rate = current/total;
 42     cnt %= len; // 因为可能发生资源加载很慢,但旋转轴依然要保持速度旋转的情况
 43     printf("[%-100s][%.1f%%][%c]\r",buffer,rate*100,table[cnt]);
 44     cnt++;
 45     fflush(stdout);
 46 }

(2) process.h

cpp 复制代码
 $ vim process.h
  1 #include<stdio.h>
  2 #include<unistd.h>
  3 void process_v1();
  4 void FlushProcess(double total,double current);

(3) main.c

cpp 复制代码
 $ vim main.c
  1 #include"process.h"
  2 #include<string.h>
  3 #include<unistd.h>
  4 
  5 double total = 1024.0;
  6 double speed = 1.0;
  7 
  8 void DownLoad()
  9 {
 10     double current = 0.0;
 11     while(current <= total)
 12     {
 13         FlushProcess(total,current);
 14         usleep(3000); // 充当下载数据
 15         current += speed;
 16     }
 17     printf("\nDownLoad %.2lfMB Done\n",current);
 18 }
 19 
 20 int main()
 21 {
 22     //process_v1();
 23     DownLoad();
 24     return 0;
 25 }

5、回调函数应用

回调函数是一种特殊的函数,它被作为参数传递给另一个函数,并在后者内部被调用。在这个代码中,DownLoad 和 UpLoad 函数都接受一个 callback_t 类型的参数,这个参数就是回调函数。它接受一个 callback_t 类型的参数 cb,这个参数指向一个回调函数。在下载过程中,DownLoad 函数和UpLoad 函数会周期性地调用这个回调函数来更新下载进度。

在 main 函数中,DownLoad 和 UpLoad 函数被调用,并且都传递了同一个回调函数 FlushProcess。

cpp 复制代码
  1 #include"process.h"
  2 #include<string.h>
  3 #include<unistd.h>
  4 
  5 typedef void (*callback_t)(double total, double current); // 函数指针
  6 
  7 double total = 1024.0;
  8 double speed = 1.0;
  9 
 10 void DownLoad(callback_t cb) 
 11 {
 12     double current = 0.0;
 13     while(current <= total)
 14     {
 15         cb(total,current); // 回调函数
 16         usleep(3000); // 充当下载数据
 17         current += speed;
 18     }
 19     printf("\nDownLoad %.2lfMB Done\n",current);
 20 }
 21 
 22 void UpLoad(callback_t cb)
 23 {
 24     double current = 0.0;
 25     while(current <= total)
 26     {
 27         cb(total,current); // 回调函数
 28         usleep(3000); // 充当下载数据
 29         current += speed;
 30    }
 31     printf("\nUpLoad %.2lfMB Done\n",current);
 32 }
 33 
 34 
 35 int main()
 36 {
 37     //process_v1();
 38     DownLoad(FlushProcess);
 39     UpLoad(FlushProcess);
 40     return 0;
 41 }
相关推荐
承渊政道9 分钟前
Linux网络学习【Reactor反应堆模式】
linux·网络·学习·ubuntu·reactor
讯捷蓝达2 小时前
戴尔R720xd服务器卡死在 “Configuring Memory...Done“,换了内存还是卡?问题根本不在内存
运维·服务器
且从容.3 小时前
ubuntu安装flink
linux·ubuntu·flink
数字新视界5 小时前
动环监控信创适配在智能机房管理中的应用价值与未来展望
大数据·服务器·数据库·动环监控系统·动力与环境监控系统
爱和冰阔落5 小时前
【Linux网络】TCP 服务器如何并发处理多个客户端?从 fork、僵尸进程到双重 fork
linux·运维·服务器·网络
程序员沉梦听雨5 小时前
WSL2的安装与使用
linux·wsl
微小的鱼5 小时前
Linux的渲染与显示系统链路
android·java·linux
lisanmengmeng5 小时前
alertmanger 标签 rules 打上分组标签
linux·运维·服务器
Android系统攻城狮6 小时前
Linux FFmpeg深度解析之av_write_frame调用流程与实战(三十三)
linux·运维·服务器·ffmpeg·音视频进阶·ffmpeg音视频
上火的金鱼妹6 小时前
k8s生产实际问题排查(网关,数据库读写等)
运维·服务器