命令行进度条完全指南:倒计时、缓冲区刷新与动态下载

进度条---命令行版本

前置知识

1.回车换行

  • \r:回车,将光标移动到当前行的开头。
  • \n:换行,将光标移动到下一行的相同位置(通常是下一行开头)。

在终端中,单独使用 \r 可以实现在同一行覆盖输出,常用于进度条或倒计时。

2.缓冲区问题

在这个程序中,如果不带\n,会发现,先休眠3s,再输出hello bit ,但我们知道程序都是从上到下执行的,那在这3s,printf去哪里了呢?答案是缓冲区。显示器都是按行刷新的,不带\n无法刷新,最后程序结束。会自动刷新缓冲区,从而输出结果。

在Linux中,一切皆文件。

在 Linux 中,一切皆文件:

  • stdin → 键盘
  • stdout → 显示器
  • stderr → 显示器(错误输出)

那怎么直接刷新输出结果呢?

用fflush

3.理解显示

关键点:显示器只认字符!任何图形、进度条等最终都要转化为字符序列输出。

4.简单倒计时程序

c 复制代码
  1 #include<stdio.h>
  2 #include<unistd.h>
  3 int main()
  4 {
  5     int n=10;
  6     while(n>=0)
  7     {
  8         printf("%-2d\r",n);//\r是为了回车,让光标回到其实位置。-是向左对其。
  9         fflush(stdout);
 10         sleep(1);
 11         n--;
 12     }
 13
 14    return 0;
 15 }

该程序目的是实现原地倒计时,效果如下

进度条

版本一:基础进度条

1.创建相关目录,及其文件

创建processbar目录,创建.h .c文件 还有main.c文件 以及makefile

Makefile内容

makefile 复制代码
 1 SRC=$(wildcard *.c)
  2 OBJ=$(SRC:.c=.o)
  3 BIN=processbar
  4 
  5 $(BIN):$(OBJ)
  6     gcc -o $@ $^
  7 %.o:%.c
  8     gcc -c $<
  9 .PHONY:
 10 clean:
 11     rm -f $(OBJ) $(BIN)
                              
2.process.c
c 复制代码
  1 #include"process.h"
  2 #include<unistd.h>
  3 #include<string.h>
  4 #define NUM 101
  5 #define STYLE '='
  6 void process_v1()
  7 {
  8     char buffer[NUM];
  9     memset(buffer,0,sizeof(buffer));
 10     const char *lable="|/-\\";
 11     int len=strlen(lable);
 12     int cnt=0;
 13     while(cnt<=100)
 14     {
 15         printf("[%s][%%%d][%c]\r",buffer,cnt,lable[cnt%len]);
 16         fflush(stdout);
 17         cnt[buffer]=STYLE;
 18         cnt++;
 19         usleep(50000);
 20     }
 21     printf("\n");
 22 }
 23 
3.process.h
c 复制代码
 1 #pragma once
  2 
  3 #include<stdio.h>
  4 
  5 void process_v1();
4.main.c
c 复制代码
  1 #include"process.h"
  2 int main()
  3 {
  4     process_v1();
  5     return 0;
  6 }
       
演示效果

这个版本只用于演示进度条的基本原理,在实际开发中无法与下载、拷贝等任务结合使用。请看下一版本。

版本二:结合下载场景的动态进度条

这个版本将进度条与一个模拟的下载任务结合,每次下载一点数据就刷新一次进度条。

main.c
c 复制代码
 1 #include"process.h"
  2 #include<stdio.h>
  3 
  4 double total =1024.0;
  5 double speed =1.0;
  6 
  7 void DownLoad()
  8 {
  9     double current = 0;
 10     while(current <= total)
 11     {
 12         FlushProcess(total,current);
 13         //下载代码
 14         usleep(10000);//充当下载数据
 15         current += speed;
 16     }
 17     printf("\ndownload %.21fMB Done\n",total);
 18 }
 19 
 20 int main()
 21 {
 22     DownLoad();
 23 //  process_v1();
 24     return 0;
 25 }
process.c
c 复制代码
  1 #include"process.h"
  2 #include<unistd.h>
  3 #include<string.h>
  4 #define NUM 101
  5 #define STYLE '='
  6 void FlushProcess(double total,double current)
  7 {
  8 
  9     char buffer[NUM];
 10     memset(buffer,0,sizeof(buffer));
 11     const char *lable="|/-\\";
 12     int len=strlen(lable);
 13 
 14     static int cnt=0;
 15     //不需要自己循环,填充#
 16     int num=(int)(current*100/total);
 17     for(int i=0;i<num;i++)
 18     {
 19         buffer[i]=STYLE;
 20     }
 21     double rate =current/total;
 22     cnt %=len;
 23     printf("[%-100s][%1.f%%][%c]\r",buffer,rate*100,lable[cnt]);
 24     cnt++;
 25     fflush(stdout);
 26 }
 27 void process_v1()
 28 {
 29     char buffer[NUM];
 30     memset(buffer,0,sizeof(buffer));
 31     const char *lable="|/-\\";
 32     int len=strlen(lable);
 33     int cnt=0;
 34     while(cnt<=100)
 35     {
 36         printf("[%s][%%%d][%c]\r",buffer,cnt,lable[cnt%len]);
 37         fflush(stdout);
 38         cnt[buffer]=STYLE;
 39         cnt++;
 40         usleep(50000);
 41     }
 42     printf("\n");
 43 }
 44 
process.h
c 复制代码
  1 #pragma once
  2 
  3 #include<stdio.h>
  4 
  5 void FlushProcess(double total,double current);
  6 void DownLoad();
  7 void process_v1();
                   
Makefile
makefile 复制代码
 1 SRC=$(wildcard *.c)
  2 OBJ=$(SRC:.c=.o)
  3 BIN=processbar
  4 
  5 $(BIN):$(OBJ)
  6     gcc -o $@ $^
  7 %.o:%.c
  8     gcc -c $< -std=c99
  9 .PHONY:
 10 clean:
 11     rm -f $(OBJ) $(BIN)
最终结果演示

为了降低代码耦合度,也可以这样写

main.c

c 复制代码
  1 #include"process.h"
  2 #include<stdio.h>
  3 
  4 double total =1024.0;
  5 double speed =1.0;
  6 
  7 typedef void (*callback_t)(double total,double current);//定义回调函数
  8 
  9 void DownLoad(callback_t cb)
 10 {
 11     double current = 0;
 12     while(current <= total)
 13     {
 14         cb(total,current);
 15         //下载代码
 16         usleep(10000);//充当下载数据
 17         current += speed;
 18     }
 19     printf("\ndownload %.21fMB Done\n",total);
 20 }
 21 
 22 int main()
 23 {
 24     DownLoad(FlushProcess);
 25 //  process_v1();
 26     return 0;
 27 }
相关推荐
Ruiery8 小时前
Linux 6.6内核内存管理深度解析(十六):mmap — 用户态“申请内存“的真相
linux·运维·服务器
欧米欧8 小时前
Linux系统篇(七),环境变量的底层溯源,核心获取机制与安全应用
linux·运维·服务器
Ruiery8 小时前
Linux 6.6内核内存管理深度解析(十八):KASLR 内核地址空间随机化
linux·运维·服务器·安全
qetfw9 小时前
UOS MariaDB 数据库:安装、最小权限与备份验证
linux·数据库·mariadb·uos
逆风的蚂蚁11 小时前
linux服务器性能调整怎么落地?我把 CPU 调度、I/O 优先级和内核参数这几个旋钮都拧了一遍
linux·运维·服务器
沫璃染墨15 小时前
从零入门计算机网络系列(十一):HTTP协议进阶——从重定向原理到GET/POST与Fiddler抓包
linux·服务器·开发语言·网络·c++·tcp/ip·html
AI_Lab0116 小时前
宝塔用了两年,有没有必要换成轻量方案?
linux·运维·服务器
by2099916 小时前
Linux GDB 调试与 CGDB 使用
linux·运维·服务器·经验分享
星恒随风17 小时前
Linux网络编程:应用层协议究竟是什么?从网络计算器到TCP Socket抽象基类完整实现
linux·网络·笔记·tcp/ip
feng_you_ying_li19 小时前
linux之网络层
linux·运维·网络