命令行进度条完全指南:倒计时、缓冲区刷新与动态下载
进度条—命令行版本前置知识1.回车换行\r回车将光标移动到当前行的开头。\n换行将光标移动到下一行的相同位置通常是下一行开头。在终端中单独使用\r可以实现在同一行覆盖输出常用于进度条或倒计时。2.缓冲区问题在这个程序中如果不带\n会发现先休眠3s再输出hello bit ,但我们知道程序都是从上到下执行的那在这3sprintf去哪里了呢答案是缓冲区。显示器都是按行刷新的不带\n无法刷新最后程序结束。会自动刷新缓冲区从而输出结果。在Linux中一切皆文件。在 Linux 中一切皆文件stdin→ 键盘stdout→ 显示器stderr→ 显示器错误输出那怎么直接刷新输出结果呢用fflush3.理解显示关键点显示器只认字符任何图形、进度条等最终都要转化为字符序列输出。4.简单倒计时程序1#includestdio.h2#includeunistd.h3intmain()4{5intn10;6while(n0)7{8printf(%-2d\r,n);//\r是为了回车让光标回到其实位置。-是向左对其。9fflush(stdout);10sleep(1);11n--;12}1314return0;15}该程序目的是实现原地倒计时效果如下进度条版本一基础进度条1.创建相关目录及其文件创建processbar目录创建.h .c文件 还有main.c文件 以及makefileMakefile内容1 SRC$(wildcard *.c) 2 OBJ$(SRC:.c.o) 3 BINprocessbar 4 5 $(BIN):$(OBJ) 6 gcc -o $ $^ 7 %.o:%.c 8 gcc -c $ 9 .PHONY: 10 clean: 11 rm -f $(OBJ) $(BIN)2.process.c1#includeprocess.h2#includeunistd.h3#includestring.h4#define NUM1015#define STYLE6voidprocess_v1()7{8charbuffer[NUM];9memset(buffer,0,sizeof(buffer));10constchar*lable|/-\\;11intlenstrlen(lable);12intcnt0;13while(cnt100)14{15printf([%s][%%%d][%c]\r,buffer,cnt,lable[cnt%len]);16fflush(stdout);17cnt[buffer]STYLE;18cnt;19usleep(50000);20}21printf(\n);22}233.process.h1#pragma once23#includestdio.h45voidprocess_v1();4.main.c1#includeprocess.h2intmain()3{4process_v1();5return0;6}演示效果这个版本只用于演示进度条的基本原理在实际开发中无法与下载、拷贝等任务结合使用。请看下一版本。版本二结合下载场景的动态进度条这个版本将进度条与一个模拟的下载任务结合每次下载一点数据就刷新一次进度条。main.c1#includeprocess.h2#includestdio.h34doubletotal1024.0;5doublespeed1.0;67voidDownLoad()8{9doublecurrent0;10while(currenttotal)11{12FlushProcess(total,current);13//下载代码14usleep(10000);//充当下载数据15currentspeed;16}17printf(\ndownload %.21fMB Done\n,total);18}1920intmain()21{22DownLoad();23// process_v1();24return0;25}process.c1#includeprocess.h2#includeunistd.h3#includestring.h4#define NUM1015#define STYLE6voidFlushProcess(doubletotal,doublecurrent)7{89charbuffer[NUM];10memset(buffer,0,sizeof(buffer));11constchar*lable|/-\\;12intlenstrlen(lable);1314staticintcnt0;15//不需要自己循环填充#16intnum(int)(current*100/total);17for(inti0;inum;i)18{19buffer[i]STYLE;20}21doubleratecurrent/total;22cnt%len;23printf([%-100s][%1.f%%][%c]\r,buffer,rate*100,lable[cnt]);24cnt;25fflush(stdout);26}27voidprocess_v1()28{29charbuffer[NUM];30memset(buffer,0,sizeof(buffer));31constchar*lable|/-\\;32intlenstrlen(lable);33intcnt0;34while(cnt100)35{36printf([%s][%%%d][%c]\r,buffer,cnt,lable[cnt%len]);37fflush(stdout);38cnt[buffer]STYLE;39cnt;40usleep(50000);41}42printf(\n);43}44process.h1#pragma once23#includestdio.h45voidFlushProcess(doubletotal,doublecurrent);6voidDownLoad();7voidprocess_v1();Makefile1 SRC$(wildcard *.c) 2 OBJ$(SRC:.c.o) 3 BINprocessbar 4 5 $(BIN):$(OBJ) 6 gcc -o $ $^ 7 %.o:%.c 8 gcc -c $ -stdc99 9 .PHONY: 10 clean: 11 rm -f $(OBJ) $(BIN)最终结果演示为了降低代码耦合度也可以这样写main.c1#includeprocess.h2#includestdio.h34doubletotal1024.0;5doublespeed1.0;67typedefvoid(*callback_t)(doubletotal,doublecurrent);//定义回调函数89voidDownLoad(callback_tcb)10{11doublecurrent0;12while(currenttotal)13{14cb(total,current);15//下载代码16usleep(10000);//充当下载数据17currentspeed;18}19printf(\ndownload %.21fMB Done\n,total);20}2122intmain()23{24DownLoad(FlushProcess);25// process_v1();26return0;27}