【Linux中的第一个小程序】进度条及printf打印彩色字符
📃博客主页: 小镇敲码人
💚代码仓库,欢迎访问
🚀 欢迎关注:👍点赞 👂🏽留言 😍收藏
🌏 任尔江湖满血骨,我自踏雪寻梅香。 万千浮云遮碧月,独傲天下百坚强。 男儿应有龙腾志,盖世一意转洪荒。 莫使此生无痕度,终归人间一捧黄。🍎🍎🍎
❤️ 什么?你问我答案,少年你看,下一个十年又来了 💞 💞 💞
Linux中的第一个小程序进度条
- \n和\r
- 倒计时程序
- 缓冲区
- 倒计时程序
- 不同平台的行结束符
- 进度条程序的编写
- 版本1 简易版
- 版本2 增强版
- printf打印彩色字体
前几篇博客介绍了C/C++中Linux系统常用的开发工具,今天这篇博客来介绍Linux中的第一个小程序进度条。
\n和\r
\n
是换行符:它会将光标下移一行。\r
是回车符:它会将光标移动到行首位置。
下面的倒计时程序加深你对
\r
回车符的认识。
倒计时程序
在写倒计时程序之前,我们先来谈一谈缓冲区的概念。
缓冲区
缓冲区是Linux中的一种机制,我们今天只简单介绍一下,它是一个文件用于存储内容,常见的有输入缓冲区和输出缓冲区。Linux中是先把一行中打印的内容存在输出缓冲区中,这样可以减少访问输出设备的次数,提高效率,等程序运行结束,或者遇到
\n
符或者一行写满,内容就会从输出缓冲区中刷新出来打印在显示器文件上。
看下面代码,思考为什么会这样:
#include <unistd.h>
#include<stdio.h>int main()
{printf("hello gcc!\n");printf("hello Linux!");printf("hello Linux!");printf("hello Linux!");printf("hello Linux!");sleep(5);return 0;
}
Linux平台会出现什么现象呢?只有第一个printf
会马上打印出来,在sleep
休眠期间,其余的printf
肯定早就跑完了,因为程序是顺序执行的,输出的字符串一定被保存到了一个地方,这个地方就是输出缓冲区,要等程序运行结束才会从缓冲区里面刷新出来:
- 每当遇到换行符时,缓冲区中的数据会被刷新到目标设备。所以第一个
printf
的内容会被刷新出来打印在显示器上,另外几个printf
由于没有遇见换行符,它们都是一行的内容,要等程序结束才会被一起刷新出来。
倒计时程序
#include <unistd.h>
#include<stdio.h>
int main()
{ for(int i = 10;i >= 0;i--) { printf("%-2d\r",i); fflush(stdout); sleep(1); } return 0;
}
fflush
函数是用来刷新缓冲区的,由于一行的内容需要写满或者\n
才会被刷新出来,所以我们需要,fflush
函数刷新缓冲区,上面已经介绍过了。\r
是回车符,会将光标移动到最开始,所以我们能将原先打印在屏幕上的内容覆盖。
运行结果:
-
%-2d
意味着以左对齐的方式输出一个至少占2个字符宽度的十进制整数,如果是一位,会填充空格,把之前显示在显示器上的内容覆盖,如果没有这个就会有内容没有被覆盖:
不同平台的行结束符
我们这里的行结束符是指的是具有回车和换行两个功能的符号,等效于Enter键。
Enter
键的符号就很明显,先换行,再回车,实际上是两步。
以下是一个关于不同平台行结束符的总结表格:
不同平台行结束符总结表格
平台 | 行结束符 | 描述 |
---|---|---|
Unix/Linux | \n (LF) | 换行符,表示当前行的结束和新行的开始 |
Windows | \r\n (CRLF) | 回车换行符,先回车(光标回到行首)再换行(光标移动到下一行) |
Mac OS 9及之前 | \r (CR) | 回车符,单独用作行结束符 |
Mac OS X及之后(包括macOS) | \n (LF) | 与Unix/Linux相同,换行符 |
在
VS2019
你会发现只用\n
也有行结束符的效果,这是是因为它具有内部处理机制来转换行结束符、允许用户通过编辑器设置指定行结束符类型,并且可能使用了广泛认可的文本处理库来处理不同平台的行结束符。
-
Windows自带的文本编辑器查看行结束符是行的,我们可以借助工具
Notepad++
。-
下载安装
Notepad++
。 -
打开要显示的文本文件。
-
点视图,勾选显示符号中的显示行尾,就可以查看行结束符:
-
CRLF
就是\r\n
,这是Windows系统处理文本行尾的标准方式。
-
-
我们还可以验证
Linux
中的行结束符,只需要在Linux
中创建一个文本文件,然后传到Windows中,继续使用这个软件即可。-
Linux
系统上创建文本文件: -
写入内容 。
-
使用指令
sz 文件的路径
,将文件快速传到Windows中。需要安装软件lrzsz
,这里Windows中接收的文件夹我们选择桌面即可。 -
传输完成之后,使用
Notepad++
打开该文件。
-
虽然Windows自带的行结束符,不能在文本文件查看器中直接显示行结束符,但是状态栏显示了它的行结束符的类型:
进度条程序的编写
版本1 简易版
下面是简易版的进度条,没有加颜色,也没有模拟真实的情况。
progress.c:
#include"progress.h"
const char* lable = "|/-\\";
//version1
void process1()
{ char buffer[NUM]; int cnt = 0; memset(buffer,'\0',sizeof(buffer[0])*NUM); int n = strlen(lable); buffer[0] = Head; while(cnt <= 100) { printf("%-100s%3d%%%c\r",buffer,cnt,lable[cnt%5]); usleep(50000);fflush(stdout); buffer[cnt++] = Body; if(cnt < 100) buffer[cnt] = Head; }
}
progress.h
#ifndef _PROGRESS_H_
#define _PROGRESS_H_
#include <time.h>
#include<stdio.h>
#include <stdlib.h>
#include<string.h>
#include<unistd.h>
#define Head '>'
#define Body '='
#define NUM 103
//version1
void process1();
#endif
main.c
#include"progress.h"
int main()
{ process1();
}
运行结果:
-
usleep
函数是Linux
系统中用于延时的函数,它会想程序挂起暂停执行,它是微秒级别的。- 在使用这个函数的时候,需要包含头文件
unistd.h
。
- 在使用这个函数的时候,需要包含头文件
-
想要打印出一个实际的百分号字符,而不是一个格式说明符,需要使用两个百分号来“转义”它。
版本2 增强版
此版本模拟实际的下载过程,进度条的进度不应该由进度条自己决定而应该由下载的程序决定,另外还增加了颜色的打印。
main.c
:
#include"progress.h" #define FILESIZE 1024*1024*1024 void download(call_back cb)
{ srand(time(NULL)^1023); int total = FILESIZE; while(total) { usleep(10000);//下载动作 int one = rand()%(1024*1024*10); total -= one; if(total < 0) total = 0; int download = FILESIZE-total; double rate = (download*1.0)/(FILESIZE)*100.0; cb(rate); }
} int main()
{ download(call_back);
}
progress.c
:
#include"progress.h"
const char* lable = "|/-\\"; char buffer[NUM] = {0};
//version2
void process_flush(double rate)
{ static int cnt = 0; if(rate <= 1.0) buffer[0] = Head; int n = strlen(lable); printf("\033[43;31;1;7m%-100s\033[0m%3lf%%%c\r",buffer,rate,lable[cnt%n]); fflush(stdout); buffer[(int)rate] = Body; if((int)rate+1 < 100) buffer[(int)(rate+1)] = Head; if(rate >= 100.0) printf("\n"); cnt++; cnt %= n;
}
progress.h
:
#ifndef _PROGRESS_H_
#define _PROGRESS_H_
#include <time.h>
#include<stdio.h>
#include <stdlib.h>
#include<string.h>
#include<unistd.h>
#define Head '>'
#define Body '='
#define NUM 103 //version2
void process_flush(double); typedef void (*call_back)(double);
#endif
运行结果:
-
我们控制了下载速度,使其每一次调用
process_flush
最多增加1%
实际情况可能更复杂。 -
cnt
是控制转动的标的,它是static
变量不会一直被初始化,所以lable
数组的下标会一直变化,下载速度不影响它的状态,这告诉用户我们的程序一直在运行,一直在尝试下载,如果不加static
就是这样:- 旋转的标不再旋转,因为
cnt
会被重新初始化为0,lable
数组的下标没变,打印的符号也不会变化。
- 旋转的标不再旋转,因为
printf打印彩色字体
基本语法:
在C语言中,使用
printf
函数打印彩色字符时,可以通过嵌入ANSI转义序列来实现。这些转义序列由特定的控制命令组成,包括以\033[
开头、以m
结尾的部分,中间则是属性码,属性代码之间使用;
分隔。
printf("\033[属性代码1;属性代码2;属性代码3....m 待写的占位符",占位符填充的变量)
常见的属性代码,我们下面用表格总结一下:
属性类别 | 属性代码 | 功能描述 | 示例 |
---|---|---|---|
通用格式控制 | 0 | 重置所有属性 | \033[0m |
1 | 高亮/加粗 | \033[1m | |
2 | 暗淡 | \033[2m | |
4 | 下划线 | \033[4m | |
5 | 闪烁 | \033[5m | |
7 | 反转 | \033[7m | |
8 | 隐藏 | \033[8m | |
前景色(文字颜色) | 30 | 黑色 | \033[30m |
31 | 红色 | \033[31m | |
32 | 绿色 | \033[32m | |
33 | 黄色 | \033[33m | |
34 | 蓝色 | \033[34m | |
35 | 品红 | \033[35m | |
36 | 青色 | \033[36m | |
背景色 | 40 | 黑色 | \033[40m |
41 | 红色 | \033[41m | |
42 | 绿色 | \033[42m | |
43 | 黄色 | \033[43m | |
44 | 蓝色 | \033[44m | |
45 | 品红 | \033[45m | |
46 | 青色 | \033[46m |
我们举几个例子来使用一下:
- 打印两行,要求第一行闪烁加粗背景色为青色文字颜色为红色,第二行为下划线,背景色为蓝色,文字颜色为品红,内容随意。
#include<stdio.h>int main()
{printf("\033[1;5;46;31m hello world!!! \n \033[0m");printf("\033[4;44;35m hello Linux!!! \033[0m");return 0;
}
\033[0m
:是重置所有属性,防止这次的内容影响到下面的打印。
- 本人知识、能力有限,若有错漏,烦请指正,非常非常感谢!!!
- 转发或者引用需标明来源。