0.前提知识
-
缓冲区:在
C/C++
语言中,会针对标准输出,给用户提供默认的缓冲区,并且根据一定的机制进行刷新(或者用户自己刷新,比如:在输出缓冲区中使用fflush(stdout)
就可以直接刷新输出缓冲区)。值得注意的是,这个缓冲区是语言给出的,而不是系统给出的。 -
换行和回车:换行和回车是两回事,但是在
C
语言的换行符\n
中,通常都会包含这两个概念(C
语言的\n
=换行+回车)a.换行,就是将光标挪至垂直位置的下一行b.回车,就是将光标挪至本行行首
1.简易实现进度条
另外,如果想要改成Windows
下运行,那么只需要修改头文件#include <unistd.h>
为#include <windows.h>
,将代码中的usleep()
改为Sleep()
,并且调整时间参数#define STIME 1000 * 1000
为#define STIME 100
即可成功在windows
环境运行下述代码。
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#define SIZE 101
#define MAX_RATE 100
#define STYLE '#'
#define STIME 1000 * 200//1000 * 1000 = 1秒
const char* str = "\\|/—";
void process();
int main()
{
process();
return 0;
}
void process()
{
int rate = 0;
char bar[SIZE] = {0};
int num = strlen(str);
while(rate <= MAX_RATE)
{
printf("[%-100s][%d%%][%c]\r", bar, rate, str[rate % num]);
fflush(stdout);
usleep(STIME);//单位为毫秒
bar[rate++] = STYLE;
}
printf("\n");
}
注意:这个代码要注意
shell
的屏幕尺寸问题,尺寸不够可能显示乱码。
2.实际开发进度条
这个版本可以好好学,在过程实践中的确有所应用。
进度条通常要和某些任务关联的,比如:下载任务。
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#define SIZE 101
#define MAX_RATE 100
#define STYLE '#'
#define STIME 1000 * 200//1000 * 1000 = 1秒
const char* str = "-\\|/";
typedef void (*callback_t)(int);
void process_plus(int rate)//参数是传递了比例,根据比例来打一段进度条
{
static char bar[SIZE];
int num = strlen(str);//旋转光标字符的长度
static int i = 0;
if (rate <= MAX_RATE && rate >= 0)
{
printf("[%-100s][%3d%%][%c]\r", bar, rate, str[i % num]);//这里换成i是为了让旋转光标能够运作,不受进度条控制,可以让用户职别这个进度条究竟是卡住了还是仍在加载
fflush(stdout);
bar[rate] = STYLE;
i++;
}
if (rate == MAX_RATE)//主要是用在下一次别的程序调用这个函数的时候,避免进度条数组中还有#
{
memset(bar, '\0', sizeof(bar));
}//这样程序可以适用与不同的任务
}
void download_1(callback_t cb)//第一个下载任务,正常调用
{
int rate = 0;
while (rate <= 100)
{
(*cb)(rate++);//回调函数
usleep(STIME);//单位为毫秒
}
printf("\n");
}
void download_2(callback_t cb)//第二个加载任务,模拟还在加载的情况,不是进度条程序卡住
{
int rate = 0;
while (rate <= 100)
{
(*cb)(rate++);//回调函数
if (rate == 50)
{
rate = 49;
}
usleep(STIME);//单位为毫秒
}
printf("\n");
}
int main()
{
download_1(process_plus);
download_2(process_plus);
return 0;
}
在上述代码中,我们新增加了一些功能:
-
让进度条随着下载任务返回的行号来实时更新进度条
-
让旋转光标的选择和进度条的循环变量
hile
分离开来,使得旋转光标可以独自旋转,而不依赖于进度条。这样就避免了用户无法分辨进度条程序是卡住了,还是处于加载的状态 -
我们模拟了两个下载任务交给
main()
执行,并且内部发送信号rate
给进度条,让用户查看两个下载任务的进度,并且第二个下载任务模拟了上面2.
的情形
3.拓张功能进度条
我们可以给进度条增添颜色或者加粗、或者替换进度条的字符、亦或者直接使用颜色块来替换进度条字符等等,请发挥您的想象力。
4.补充Windows下的代码
虽然我在开头说明了如何修改成Windows
下也可以运行的代码,但是我担心您对开头那段话有所误解,因此特意重新修改为在Windows
下运行的代码。
#include <stdio.h>
#include <string.h>
//#include <unistd.h>
#include <windows.h>
#define SIZE 101
#define MAX_RATE 100
#define STYLE '#'
#define STIME 100//1000 * 1000 = 1秒
const char* str = "-\\|/";
typedef void (*callback_t)(int);
void process_plus(int rate)//参数是传递了比例,根据比例来打一段进度条
{
static char bar[SIZE];
int num = strlen(str);//旋转光标字符的长度
static int i = 0;
if (rate <= MAX_RATE && rate >= 0)
{
printf("[%-100s][%3d%%][%c]\r", bar, rate, str[i % num]);//这里换成i是为了让旋转光标能够运作,不受进度条控制,可以让用户职别这个进度条究竟是卡住了还是仍在加载
fflush(stdout);
bar[rate] = STYLE;
i++;
}
if (rate == MAX_RATE)//主要是用在下一次别的程序调用这个函数的时候,避免进度条数组中还有#
{
memset(bar, '\0', sizeof(bar));
}//这样程序可以适用与不同的任务
}
void download_1(callback_t cb)//第一个下载任务,正常调用
{
int rate = 0;
while (rate <= 100)
{
(*cb)(rate++);//回调函数
Sleep(STIME);//单位为毫秒
}
printf("\n");
}
void download_2(callback_t cb)//第二个加载任务,模拟还在加载的情况,不是进度条程序卡住
{
int rate = 0;
while (rate <= 100)
{
(*cb)(rate++);//回调函数
if (rate == 50)
{
rate = 49;
}
Sleep(STIME);//单位为毫秒
}
printf("\n");
}
int main()
{
download_1(process_plus);
download_2(process_plus);
return 0;
}