彩色进度条
完成这个小程序需要一些预备知识:
- Linux下的色彩是如何编码的?
- 进度条以什么样的形式展现?如何让printf?
- 理解计算机缓冲区的概念。
##问题一
在Linux下编程不能用到<windows.h>的头文件,那怎么实现彩色,我们需要研究一下Linux彩色打印。
Linux终端会解析一些,并且依据控制码来设置终端的绘制属性,所以只要输出流中包含ANSI控制码
就可以工作。
echo -e "\033[31m 字体\33[0m" //红色的ANSI控制码是\033[31m
从\033[31m 处开始使用红色作为字体的前景色,后面的绘制都使用红色,直到遇到属性关闭控制码,所以后面使用\033[0m 来关闭属性。
多个控制码一起使用,可以看到叠加效果,控制码之间顺序无所谓,不管叠加多少个,其关闭控制码仍然是\033[0m
控制码如下:
这里顺便用红色标记出我们编写progress bar 所需要的控制码
ANSI属性控制 | 功能 |
---|---|
\033[0m | 关闭所有属性 |
\033[1m | 设置高亮度 |
\033[4m | 下划线 |
\033[5m | 闪烁 |
\033[7m | 反显 |
\033[8m | 消隐 |
\033[30m – \033[37m | 设置前景色 |
\033[40m – \033[47m | 设置背景色 |
\033[nA | 光标上移n行 |
\033[nB | 光标下移n行 |
\033[nC | 光标右移n列 |
\033[nD | 光标左移n列 |
\033[y;H | 设置光标位置 |
\033[2J | 清屏 |
\033[K | 清除从光标到行尾的内容 |
\033[s | 保存光标位置 |
\033[u | 恢复光标位置 |
\033[?25l | 隐藏光标 |
\033[?25h | 显示光标 |
-
文字背景色彩数字:(颜色范围:40-49)
- 40:黑色
- 41: 深红色
- 42: 绿色
- 43: 黄色
- 44: 蓝色
- 45: 紫色
- 46: 深绿色
- 47: 白色
-
文字前景色数字:(颜色范围:30 - 39)
- 30: 黑色
- 31: 红色
- 32: 绿色
- 33: 黄色
- 34: 蓝色
- 35: 紫色
- 36: 深绿色
- 37: 白色
问题二
一般进度条要给人视觉上的停留且应该连续进行打印,我们需要printf若干个符号#,sleep函数让进度条给人停留感。
还有关于换行符\n和回车符\r的区别;
符号 | ASCII码 | 意义 |
---|---|---|
‘\r’ | 10 | 回车,使光标到行首(carriage return) |
‘\n’ | 13 | 换行,使光标下移一格(line feed) |
在不同的操作系统这几个字符表现不同,比如在WIN系统下,这两个字符就是表现的本义,在UNIX类系统,换行\n就表现为光标下一行并回到行首,在MAC上,\r就表现为回到本行开头并往下一行,至于ENTER键的定义是与操作系统有关的。通常用的Enter是两个加起来。
而在打印应该使用回车,不能用换行,因为进度条只能是一行,不能跳到下一行去,从打印一个#开始,回车,光标回到这一行的行首,每次多打印一个#,将覆盖到前一次打印的光标,而且每次多一次。
-
关于sleep函数和 usleep 函数
头文件是<unistd.h>,定义函数:
- unsigned int sleep(unsigned int seconds);
- sleep 函数会令目前的进程暂停,直到到达参数seconds所指定的秒数,或是被信号中断。
- void usleep(unsigned long usec);
- usleep 函数参数单位是微秒
问题三_缓冲区
计算机为了提高CPU的效率,同时减少程序的等待时间,还有一个原因,CPU并不直接操作硬件设备,驱动程序才直接操作硬件,而进行交互动作时,中间总要经过复杂的步骤,所以缓冲区,作为内存的一部分,存在是很有必要的。
缓冲区分为全缓冲、行缓冲、不带缓冲。
编写进度条的程序,我们用到行缓冲,Linux下的printf函数是带有行缓冲的,只有遇见换行符才会刷新缓冲区,而前面我们提到,进度条是不能用换行符的,只能用回车,所以就需要我们手动刷新缓冲区fflush(stdot)函数可以帮我们完成。
关于缓冲区的介绍,这篇博文里有介绍缓冲区.
#include<stdio.h> #include<unistd.h>
#include<string.h>
int main(void){
char array[101];
memset(array, 0, sizeof(array));
int i = 0;
int color;
const char* lable = "|/-\\";
for(; i <= 100; i++){
color = i % 6;
color++;
array[i] = ' ';
switch(color){
case 1: printf("\033[25l\033[41m%s\x0d\033[1m\033[37m%d%%\33[0m", array, i);
break;
case 2:
printf("\033[?25l\033[45m%s\x0d\033[1m\033[37m%d%%\33[0m", array, i);
break;
case 3:
printf("\033[?25l\033[44m%s\x0d\033[1m\033[37m%d%%\33[0m", array, i);
break;
case 4:
printf("\033[?25l\033[42m%s\x0d\033[1m\033[37m%d%%\33[0m", array, i);
break;
case 5:
printf("\033[?25l\033[46m%s\x0d\033[1m\033[37m%d%%\33[0m", array, i);
break;
case 6:
printf("\033[?25l\033[40m%s\x0d\033[1m\033[37m%d%%\33[0m", array, i);
break;
}
fflush(stdout);
usleep(100000);
}
printf("\n");
return 0;
}
- gif帧数小,实际进图条的渐变比较连续
- 如果你的电脑回车符达不到想要的效果,请换成回车符的ASCII码值。
- 数组一定要初始化,不然你不知道,数组里面是否会遇到一个换行符,以外的刷新缓冲区。
- 巧用取模运算。
- 另外,如果你的界面放不下100个字符,需要将界面的字体调小:View->Zoom Out
需要改进的地方:
是否能用宏替换代替复杂的控制码,以及优化程序中重复语句。