[Linux]制作进度条小程序
文章目录
- [Linux]制作进度条小程序
- C语言中的\n和\r字符
- 缓冲区的刷新策略
- 进行进度条代码编写
C语言中的\n和\r字符
C语言中字符分为两种:
- 可显字符
- 控制字符
其中可显字符就是字符a这类的字符,控制字符就是\n这种控制字符。
对于我们制作进度条,我们只需要关注两个控制字符:
- \r – 进行回车操作
- \n – 进行换行加回车操作
说明: \n本身是换行字符,但是C语言本身将其解析成了换行加回车。
为了更好地体会字符\r和\n的作用,我们需要做一些测试,为了方便进行编译,创建makefile文件,文件内容如下:
mytest:test.c
gcc -o mytest test.c
.PHONY:clean
clean:
rm -f mytest
首先执行如下代码:
#include <stdio.h>
#include <unistd.h>
int main()
{
printf("hello world");
sleep(3);
fflush(stdout);
return 0;
}
说明:
- fflush(stdout)是将标准输出缓冲区刷新,便于观察现象。
- sleep为Linux系统的休眠函数。
执行结果如下:
在打印完hello world
后,程序休眠,"光标"在同一行的下一个位置
休眠结束后,将会接着从光标位置开始打印,因此打印的[qxm@aliyun-centos review]$
命令行提示符,紧跟在hello world
其后。
再执行如下代码:
#include <stdio.h>
#include <unistd.h>
int main()
{
printf("hello world\n");
sleep(3);
return 0;
}
执行结果如下:
由于\n被C语言解析成换行加回车,在打印完hello world
后程序休眠,"光标"会到下一行的开始的位置。
休眠结束后,将会接着从光标位置开始打印,因此[qxm@aliyun-centos review]$
命令行提示符是在下一行的行首打印。
最后执行如下代码:
#include <stdio.h>
#include <unistd.h>
int main()
{
printf("hello world\n");
sleep(3);
return 0;
}
说明:
- fflush(stdout)是将标准输出缓冲区刷新,便于观察现象。
- sleep为Linux系统的休眠函数。
执行结果如下:
由于\r为回车,在打印完hello world
后程序休眠,"光标"会回到行首。
休眠结束后,将会接着从光标位置开始打印,因此[qxm@aliyun-centos review]$
命令行提示符将原有的hello world
覆盖了。
缓冲区的刷新策略
Linux系统下,C语言会将要打印的字符先存放在缓冲区中,只有将缓冲区内的字符刷新到屏幕上,才能在屏幕上看到,缓冲区刷新的情况如下:
- 遇到\n会将\n前的所有字符刷新到屏幕上。
- 程序结束时自动刷新缓冲区。
对于缓冲区测试,我们执行如下代码:
#include <stdio.h>
#include <unistd.h>
int main()
{
printf("hello world\r");
sleep(3);
return 0;
}
执行结果如下:
因为缓冲区没有刷新,因此程序休眠时,没有任何打印。
程序执行结束后,缓冲区被自动刷新,hello world
被打印出来,但是由于\r回车将"光标"退回到行首,因此命令提示符的打印将前面的打印覆盖了。
再执行如下代码:
#include <stdio.h>
#include <unistd.h>
int main()
{
printf("hello world\n");
sleep(3);
return 0;
}
执行结果如下:
由于遇到了\n缓冲区内的数据在休眠前刷新了。
程序休眠结束后,命令行提示符从光标所在位置开始打印。
进行进度条代码编写
创建以下文件构成代码结构:
- myproc.h – 保存进度条代码的声明
- myproc.c – 保存进度条代码的实现
- main.c – 调用进度条代码
myproc.h文件中的核心结构如下:
#pragma once
#include <stdio.h>
extern void process();
myproc.c文件中的核心结构如下:
#include "myproc.h"
void process()
{
//...
}
main.c文件中的核心结构如下:
#include "myproc.h"
int main()
{
process();
return 0;
}
同时创建makefile文件,makefile文件内部写入如下内容:
myproc:myproc.c main.c
gcc -o myproc myproc.c main.c
.PHONY:clean
clean:
rm -f myproc
建立好代码结构后,编写如下代码充当图形化进度条:
#include "myproc.h"
#include <string.h>
#include <unistd.h>
#define STYLE '='
#define ARROW '>'
#define SIZE 101
void process()
{
char buf[SIZE];
memset(buf, 0 , SIZE);
int i = 0;
while(i <= 100)
{
printf("[%-100s]\r", buf);
fflush(stdout);
buf[i++] = STYLE;
if(i != 100 )buf[i] = ARROW;
usleep(100000);
}
printf("\n");
}
首先创建一个字符串用于保存要打印的图形化进度条buf,对其进行初始化,然后在打印时进行左对齐打印buf并且按100个字符长度打印,
每次打印完回车覆盖上一次从打印,并且刷新缓冲区使得打印显示到屏幕上,使用休眠函数来充当进度的加载。
打印的效果如下:
除了图形化进度条还要设置数字化进度显示,因此需要对打印进行修改,如下:
printf("[%-100s][%d%%]\r", buf, i);
增加打印进度百分比作为数据化进度显示,%%将会转义成一个%打印在屏幕上。
打印的效果如下:
最后增添一个旋转光标表示进度条正在不断执行,因此需要对打印再进行修改,得到最终的代码如下:
#include "myproc.h"
#include <string.h>
#include <unistd.h>
#define STYLE '='
#define ARROW '>'
#define SIZE 101
void process()
{
const char* cursor = "|/-\\";
char buf[SIZE];
memset(buf, 0 , SIZE);
int i = 0;
while(i <= 100)
{
printf("[%-100s][%d%%][%c]\r", buf, i, cursor[i%4]);
fflush(stdout);
buf[i++] = STYLE;
if(i != 100 )buf[i] = ARROW;
usleep(100000);
}
printf("\n");
}
添加了cursor字符串保存旋转光标的样式,其中\\会转义成一个\,因为旋转光标中是将4个字符循环打印因此将样式字符串模4输出。
最终进度条的演示结果如下: