目录
该篇博客会主要按步骤推导出一个在Linux上运行的进度条小程序,会用到vim编辑器和gcc编译器,如果对这两个软件不熟悉,可以点击链接,结合该篇博客学习,进度条小程序展示如下:
一.\r && \n
c语言有很多的字符,但宏观上可以分为两种:可显字符、控制字符
可显字符:A、a、B、c…等字符
控制字符:\n(回车)、\t(水平制表)、\r(换行)等
这里我们需要在Linux系统下使用
\r
和
\n
两字符
一般,我们在C语言中使用
\n
进行换行,或在使用电脑的Enter键进行换行,是直接将其换到下一行的最左侧处
intmain(){printf("Hello\nWorld!");return0;}
但事实上,这是两个动作,只是在语言的范畴,C语言使用
\n
完成了换到下一行和回到当前最左侧这两个动作。
像我们之前的老式键盘,回车键与现在的回车键不同,表示该键位是两个动作的和。
所以我们平时所指的换行都是由这两个动作组成的。
了解了上面的知识,我们在来看一下
\r
和
\n
的意义
\r:回车(回到当前行最左侧)
\n:换行(换到下一行)
在我们使用C语言进行
\n
换行,在语言层面,默认进行了换行+回车两个动作
二.行缓存区概念
问题:
我们首先观察下面两个段代码的在Linux下运行后的区别
- 会将两段代码的可执行文件都命名位MyTest,并运行展示
- 代码中会用到
sleep
函数,可以使用man指令,在3号手册中查找(Linux常见指令)man 3 sleep
- 作用:在当前代码处暂停,单位秒。
代码1
#include<stdio.h>#include<unistd.h>intmain(){printf("hello world\n");//使用换行符'\n'sleep(3);//暂停3秒后继续运行return0;}
代码2
#include<stdio.h>#include<unistd.h>intmain(){printf("hello world\r");//使用回车符'\r'sleep(3);//暂停3秒后继续运行return0;}
按照我们正常的逻辑,两段代码都应该打印出东西,但是代码2只执行了sleep,什么都没有打印出来,这是为什么?是不是和
\r
、
\n
有关系呢?
这个问题就涉及到了缓存区的概念(我们这里简单的了解一下,缓冲区剩余的内容会在之后的博客中)
解答:
我们在来看一下,下面这段代码及其运行结果:
代码3
#include<stdio.h>#include<unistd.h>intmain(){printf("hello world");sleep(3);//暂停3秒后继续运行return0;}
我们可以看到上面的结果,好像这次是先执行了sleep函数,在执行了printf,又在输出的字符串后,输出了命令行。
- C语言的代码执行的是顺序结构,必须按顺序和代码逻辑依次执行,所以一定是printf先执行,然后是sleep。
- printf即被执行,需要有一个地方临时存放字符串,这个地方就是缓存区。
- 当代码被执行完,缓存区被刷新,字符串被打印到屏幕上,此时光标(绿色光标)的位置在字符串的后面。
- 在Linux中,每次像显示器打印数据都是从光标的位置开始,即:光标和显示器匹配,光标在哪里,就在哪里开始打印。
- 所以命令行会直接打印在字符串后面。(代码1进行了换行,光标在新的一行)
- 不同的平台缓冲区的表现形式不同,在Linux中,缓存区有自己的刷新策略(很多)我们现在看的是行缓存,它会在六种情况下刷新缓存区(只介绍三条,其余内容与该文无关)1. 遇到换行符,如:
\n
。(代码1先看到字符串后sleep的原因)2. 程序结束的时候。(代码三的情况)3. 主动刷新
从上面的内容,我们就能分析除代码2执行不成功的原因:
printf函数执行后,并没有使缓冲区刷新,数据保存在缓冲区内,执行sleep后,字符串在显示器上打印,但在之前的内容中我们指定
\r
为回车,将光标返回到这一行的最左边,然后命令行在光标开始处打印,将字符串内容覆盖。我们什么都看不到了。
那有办法解决这个问题吗?答:我想不出来,只要我们还将
\r
放在字符串最后面,字符串终会被命令行覆盖,即使我们使用其他控制字符,显示出结果,但结果终究和我们想要的不同。
这里我们倒是可以利用上面介绍的刷新缓存的第三种方法,来检测我们的解答是否正确,看到字符串在屏幕上出现并消失。
检测:
我们要主动刷新缓冲区,需要使用
fflush
函数,我们同样可以用man指令在3号手册中查找。Linux常见指令
man 3 fflush
测试代码如下:
#include<stdio.h>#include<unistd.h>intmain(){printf("hello world\r");//使用回车符'\r'fflush(stdout);//自动刷新缓存区sleep(3);//暂停3秒后继续运行return0;}
我们从结果看出,事实就是如此,它的光标移到了最左边,命令行打印时将其覆盖,我们在代码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>#defineN101#defineSTYLE'='intmain(){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");return0;}
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>#defineN101#defineSTYLE'='intmain(){char bar[N];memset(bar,'\0',sizeof(bar));constchar* 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");return0;}
关于进度条颜色和形状不仅仅只有这些,大家感兴趣可以去搜索更多的内容,自己创建一个特别的进度条。
版权归原作者 榶曲 所有, 如有侵权,请联系我们删除。