一,进度条的必备知识
1,缓冲区的粗略介绍
缓冲区是内存的一部分空间,用于临时存储输入和输出的数据。它可分为输入缓冲区和输出缓冲区。每当我们输入数据时都是往输入缓冲区中存放数据,当刷新缓冲区时,数据将会从缓冲区中拿出输入到某个变量中。每当我们输出数据时,系统将会把数据输出到输出缓冲区中,当刷新输出缓冲区时,数据将会从输出缓冲区输出到指定地方。
其中,缓冲区的刷新时机是不同的。行缓冲会在遇到换行符时刷新,全缓冲会在缓冲区写满时刷新,而无缓冲则没有缓冲区,代表是系统调用。在C/C++中,通常用 fflush(FILE* stream) 来强制刷新指定流的缓冲区。
C/C++中类似于sleep函数功能控制的就是缓冲区,当系统调用到sleep是,将会被缓冲区暂时保存起来,一旦sleep运行完毕之后缓冲区才刷新。进度条有时控制的就是缓冲区的刷新时间。
2,回车与换行
“ 回车 ” 是把光标从当前位置直接指向最开头位置。“ 换行 ” 是把光标从当前位置直接指向下一行同一列的位置。我们在C语言阶段常用的 “ \n ” 指的是 换行 + 回车。而 “ \r ” 只表示回车。
二,进度条的初步制作
1,进度条的初步矿建
首先,我们先来编写进度条的简单倒计时程序,这就需要运用回车和sleep来控制程序的运行。
#include <iostream> #include <iomanip> //setw的头文件 #include <unistd.h> //usleep()的头文件,对应参数单位为微秒 #include <cstdio> using namespace std; int main() { int n = 10; while (n >= 0) { cout << left << setw(2) << n << '\r'; //跟C语言中printf("%-2d\r", n)效果一样 fflush(stdout); //强制刷新输出缓冲区 n--; usleep(500000); //这里我们控制缓冲时间为0.5秒 } cout << endl; return 0; }
下一步,要思考进度条的框架设计。这里的进度条将外围用 " = " 表示进度的加载,外围设置了百分比显示加载数据。用 "|/-\" 来表示其中的加载,即顺时针旋转。
2,进度条的版本一
首先,外面设置一个头文件 "process.h" 进行必要的设置
#include <iostream> #include <string> #include <unistd.h> #include <iomanip> #include <cstdio> using namespace std; #define Body '=' //使用body来表示进度 #define Head '>' //Head表示目前加载的终点,这里用 ' > ' 表示 void process1(); //进度条函数
下面,进行进度功能的编写。这里使用 usleep 功能来控制进度的的运行,这里需注意的是输出缓冲区的刷新。
void process1() { //用lable表示进度条的加载 string lable("|/-\\"); string nums; int count = 0; int lablesize = lable.size(); nums.push_back(Head); while (count <= 100) { cout << "[" << left << setw(100) << nums << "]"; cout << "[" << "%" << count << "]"; cout << "[" << lable[count % lablesize] << "]" << '\r'; fflush(stdout); nums.clear(); count++; nums.append(count, Body); if (count < 100) { nums.push_back(Head); } //这里我们设置每0.6秒加载一次 usleep(60000); } cout << endl; }
运行最终结果:
[====================================================================================================][%100][|]
3,进度条的版本二
进度条一般都是运用在一种应用上,表示应用的加载过程。很显然,版本一的进度条只是无脑运行,不知道程序进度是多少,即没有依附应用进度,比如下载程序,这时的进度条需依附于下载进度来跟进。
头文件 "process.h" 添加如下:
#include <iostream> #include <string> #include <unistd.h> #include <iomanip> #include <cstdio> #include <cstdlib> using namespace std; #define Body '=' #define Head '>' #define Max 103 #define FileSize 1024*1024*1024 //设置FileSize文件内存为1G,表示下载程序的总大小 typedef void (*callback_t)(double); //利用函数指针来进行封装进度运用 void download(callback_t); //模拟一种下载进度 void process2(double rate); //进度条跟进程序
这里,在设置download下载时要将每一次的下载进度传递给进度条让其显示百分比。
void download(callback_t cb) //利用回调函数的形式设置进度 { srand(time(0)*1024); int total = FileSize; while (total) { //下面表示一次下载动作 usleep(10000); int one = rand() % (1024 * 1024 * 5); total -= one; if (total < 0) { total = 0; } //表示当前的进度 int download = FileSize - total; double rate = (download * 1.0 / (FileSize)) * 100.0; cb(rate); //每一次进度条的传递 } }
进度条设置时要说明以下几点:
1,我们使用 "|/-\\" 表示进度跟进时是根据下载进度进行的,与当前的进度无关。
2,进度条的总设置需与下载程序紧紧联系。比如当程序加载完时,“ > ” 进度条中表示进 度运行的就要停止,即删除。
3,在输出进度运行过程,我们可添加其色彩表示美观,链接:色彩文本的增添
void process2(double rate) { //用lable表示下载任务一直在跟进 string lable("|/-\\"); //注意,这里要保留之前的进度,需设置静态 static char buffer[Max] = { 0 }; static int cnt = 0; if (rate <= 1.0) { buffer[0] = Head; } printf("\033[1;31;46m[%-100s]\033[0m[%.1lf%%][%c]\r", buffer, rate, lable[cnt % lable.size()]); //设置色彩,这里我们设置高亮/加粗,青色背景,红色字体的色彩 fflush(stdout); //下面控制进度的跟进 buffer[(int)rate] = Body; if ((int)rate + 1 < 100) { buffer[(int)(rate + 1)] = Head; } if (rate >= 100.0) { cout << endl; } cnt++; cnt %= lable.size(); }
总代码如下:
#include "process.h" //版本一 void process1() { string lable("|/-\\"); string nums; int count = 0; int lablesize = lable.size(); nums.push_back(Head); while (count <= 100) { cout << "[" << left << setw(100) << nums << "]"; cout << "[" << "%" << count << "]"; cout << "[" << lable[count % lablesize] << "]" << '\r'; fflush(stdout); nums.clear(); count++; nums.append(count, Body); if (count < 100) { nums.push_back(Head); } usleep(60000); } cout << endl; } //版本二 void download(callback_t cb) { srand(time(0) * 1024); int total = FileSize; while (total) { usleep(10000); int one = rand() % (1024 * 1024 * 5); total -= one; if (total < 0) { total = 0; } int download = FileSize - total; double rate = (download * 1.0 / (FileSize)) * 100.0; cb(rate); } } void process2(double rate) { static string lable("|/-\\"); static char buffer[Max] = { 0 }; static int cnt = 0; if (rate <= 1.0) { buffer[0] = Head; } printf("\033[1;31;46m[%-100s]\033[0m[%.1lf%%][%c]\r", buffer, rate, lable[cnt % lable.size()]); fflush(stdout); buffer[(int)rate] = Body; if ((int)rate + 1 < 100) { buffer[(int)(rate + 1)] = Head; } if (rate >= 100.0) { cout << endl; } cnt++; cnt %= lable.size(); } int main() { //process1(); //使用进度条粗略版本一 download(process2); //使用进度条进化版本二 return 0; }
最后,要说明的是,以上程序都是在Linux系统下运行进行的,在VS或其它编译器下可能会出现错误消息,这时因为不同平台支持的C标准或系统设置不同而造成的差异。