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

进度条---命令行版本

前置知识

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 }
相关推荐
爱看老照片10 分钟前
linux上查看磁盘空间占用情况,清理大文件
linux·清理·大文件·磁盘空间
你是个什么橙32 分钟前
Linux 远程桌面访问和管理——VNC服务器
linux·运维·服务器
nhfc9932 分钟前
whisper.cpp编译
linux·运维·服务器
载数而行5201 小时前
Linux 8 进程(树)相关指令
linux
ShineWinsu1 小时前
对于Linux:内核是如何组织管理IPC资源的解析
linux·服务器·c++·面试·笔试·线程·ipc
feng_you_ying_li1 小时前
Linux之线程同步:条件变量和两种生产消费模型
linux·运维·服务器
Dlrb12111 小时前
Linux系统编程-线程与多线程模块的封装
linux·线程·互斥锁·线程同步·线程互斥
拾贰_C1 小时前
【Ubuntu | VSCode | SSH | 远程连接 | Linux】VSCode 怎么实现ssh远程连接
linux·vscode·ubuntu
一叶知秋dong2 小时前
llama.cpp 启动脚本
linux·服务器·llama
桌面运维家2 小时前
校园机房vDisk IDV云桌面建设方案价格参考
linux·服务器·数据库