【Linux】进度条小程序
阿里云国内75折 回扣 微信号:monov8 |
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6 |
目录
该篇博客会主要按步骤推导出一个在Linux上运行的进度条小程序会用到vim编辑器和gcc编译器如果对这两个软件不熟悉可以点击链接结合该篇博客学习进度条小程序展示如下
一.\r && \n
c语言有很多的字符但宏观上可以分为两种可显字符、控制字符
可显字符A、a、B、c…等字符
控制字符\n(回车)、\t(水平制表)、\r(换行)等
这里我们需要在Linux系统下使用\r
和\n
两字符
一般我们在C语言中使用\n
进行换行或在使用电脑的Enter键进行换行是直接将其换到下一行的最左侧处
int main()
{
printf("Hello\nWorld!");
return 0;
}
但事实上这是两个动作只是在语言的范畴C语言使用\n
完成了换到下一行和回到当前最左侧这两个动作。
像我们之前的老式键盘回车键与现在的回车键不同表示该键位是两个动作的和。
所以我们平时所指的换行都是由这两个动作组成的。
了解了上面的知识我们在来看一下\r
和\n
的意义
\r回车回到当前行最左侧
\n换行换到下一行
在我们使用C语言进行\n
换行在语言层面默认进行了换行+回车两个动作
二.行缓存区概念
问题
我们首先观察下面两个段代码的在Linux下运行后的区别
-
会将两段代码的可执行文件都命名位MyTest并运行展示
-
代码中会用到
sleep
函数可以使用man指令在3号手册中查找Linux常见指令man 3 sleep
- 作用在当前代码处暂停单位秒。
代码1
#include<stdio.h>
#include<unistd.h>
int main()
{
printf("hello world\n");//使用换行符'\n'
sleep(3);//暂停3秒后继续运行
return 0;
}
代码2
#include<stdio.h>
#include<unistd.h>
int main()
{
printf("hello world\r");//使用回车符'\r'
sleep(3);//暂停3秒后继续运行
return 0;
}
按照我们正常的逻辑两段代码都应该打印出东西但是代码2只执行了sleep什么都没有打印出来这是为什么是不是和
\r
、\n
有关系呢
这个问题就涉及到了缓存区的概念我们这里简单的了解一下缓冲区剩余的内容会在之后的博客中
解答
我们在来看一下下面这段代码及其运行结果
代码3
#include<stdio.h>
#include<unistd.h>
int main()
{
printf("hello world");
sleep(3);//暂停3秒后继续运行
return 0;
}
我们可以看到上面的结果好像这次是先执行了sleep函数在执行了printf又在输出的字符串后输出了命令行。
-
C语言的代码执行的是顺序结构必须按顺序和代码逻辑依次执行所以一定是printf先执行然后是sleep。
-
printf即被执行需要有一个地方临时存放字符串这个地方就是缓存区。
-
当代码被执行完缓存区被刷新字符串被打印到屏幕上此时光标绿色光标的位置在字符串的后面。
-
在Linux中每次像显示器打印数据都是从光标的位置开始即光标和显示器匹配光标在哪里就在哪里开始打印。
-
所以命令行会直接打印在字符串后面。代码1进行了换行光标在新的一行
-
不同的平台缓冲区的表现形式不同在Linux中缓存区有自己的刷新策略很多
我们现在看的是行缓存它会在六种情况下刷新缓存区只介绍三条其余内容与该文无关
- 遇到换行符如
\n
。代码1先看到字符串后sleep的原因 - 程序结束的时候。代码三的情况
- 主动刷新
- 遇到换行符如
从上面的内容我们就能分析除代码2执行不成功的原因
printf函数执行后并没有使缓冲区刷新数据保存在缓冲区内执行sleep后字符串在显示器上打印但在之前的内容中我们指定
\r
为回车将光标返回到这一行的最左边然后命令行在光标开始处打印将字符串内容覆盖。我们什么都看不到了。
那有办法解决这个问题吗答我想不出来只要我们还将\r
放在字符串最后面字符串终会被命令行覆盖即使我们使用其他控制字符显示出结果但结果终究和我们想要的不同。
这里我们倒是可以利用上面介绍的刷新缓存的第三种方法来检测我们的解答是否正确看到字符串在屏幕上出现并消失。
检测
我们要主动刷新缓冲区需要使用fflush
函数我们同样可以用man指令在3号手册中查找。Linux常见指令
man 3 fflush
测试代码如下
#include<stdio.h>
#include<unistd.h>
int main()
{
printf("hello world\r");//使用回车符'\r'
fflush(stdout);//自动刷新缓存区
sleep(3);//暂停3秒后继续运行
return 0;
}
我们从结果看出事实就是如此它的光标移到了最左边命令行打印时将其覆盖我们在代码3中看不到结果。
拓展
凡是向显示器打印的所有的内容都是字符
printf("%d",123);
//打印的123分别为'1'、'2'、'3'
三.进度条
展示效果
我们可以利用上面缓存区的知识使用C语言在Linux上实现这样的一个小程序
一个这样的小程序我们可以简单将其分为如下图的四个部分
1.进度动态条
进度条的增长的图形我设置为 =
并以 >
符号开头
进度条的设置是从0%到100%0%无进度我们需要大小100个=
还需要1个 >
在最后加一个终止符\0
共102个字节存储也就是存储进度条的char类型的数组大小为102。
注意创建了数组后需要对其继续初始化使其102个字节都为\0
方便每次到达更新的位置后准确停下。
我们要它每加载百分之一就刷新一次我们需要将要打印的数组放在循环内将\r
放在要打印的字符串后面在使用fflush(stdout)
来自动刷新缓存区在使用usleep
sleep单位秒usleep单位微妙可以自己尝试一下看看用那个更合适将每次循环暂停一下在继续这样就能展示出进度条上涨的形状。
2.进度百分比
我们在循环被打印进度条只要打印时在将循环的次数打印即可注意百分号表示为%%
不能使用\%
会报警有先平台上编译会显示失败。
修改上述代码如下
printf("[%-100s][%d%%]\r",bar,i);
3.小装饰
在进度条的最后我们增加了一个旋转的光标使其看着更加生动
这里使用| / - \
四个符号来表示光标将其存入数组注意\表示转移字符要使用两个\来表示一个
顺时针旋转“|/-\”
逆时针旋转“|\-/”
这里使用顺时针完整的修改代码如下
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#define N 101
#define STYLE '='
int main()
{
char bar[N];
memset(bar,'\0',sizeof(bar));
char lable[4] = "|/-\\";
int i=0;
while(i<=100)
{
printf("[%-100s][%d%%][%c]\r",bar,i,lable[i%4]);
fflush(stdout);
usleep(100000);
bar[i++] = STYLE;
if(i!=100) bar[i] = '>';
}
printf("\n");
return 0;
}
4.颜色
想要使我们输出的进度条改变颜色有很多种方法这里我只展示一种有兴趣的可以自己去搜索
//格式
printf("\e[31;42m字符串\e[0m");
//31:前景色
//42:后景色
//\e[31 开头
//\e[0m 终止使改变的颜色只在字符串内
前景色字体颜色
字符 | 颜色 |
---|---|
30 | 黑色 |
31 | 红色 |
32 | 绿色 |
33 | 黄色 |
34 | 蓝色 |
35 | 紫色 |
36 | 深绿 |
37 | 白色 |
背景色
字符 | 颜色 |
---|---|
40 | 黑色 |
41 | 红色 |
42 | 绿色 |
43 | 黄色 |
44 | 蓝色 |
45 | 紫色 |
46 | 深绿 |
47 | 白色 |
有了这些知识我们就能改变进度条的颜色将其变为红色代码如下
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#define N 101
#define STYLE '='
int main()
{
char bar[N];
memset(bar,'\0',sizeof(bar));
const char* lable = "|/-\\";
int i=0;
while(i<=100)
{
printf("[\033[31m%-100s\033[0m][%d%%][%c]\r",bar,i,lable[i%4]);
fflush(stdout);
usleep(100000);
bar[i++] = STYLE;
if(i!=100) bar[i] = '>';
}
printf("\n");
return 0;
}
关于进度条颜色和形状不仅仅只有这些大家感兴趣可以去搜索更多的内容自己创建一个特别的进度条。