【linux】倒计时小程序、进度条小程序及其puls版本

发布于:2025-05-15 ⋅ 阅读:(18) ⋅ 点赞:(0)

小编个人主页详情<—请点击
小编个人gitee代码仓库<—请点击
linux系列专栏<—请点击
倘若命中无此运,孤身亦可登昆仑,送给屏幕面前的读者朋友们和小编自己!
在这里插入图片描述



前言

【linux】linux项目自动化构建工具-make/Makefile—书接上文详情请点击<—
本文由小编为大家讲解—倒计时小程序、进度条小程序


一、知识铺垫

1. 回车换行

我们键盘上的enter键其实作用是回车并换行,回车是指让当前的光标移动至当前行的起始位置,换行是换到下一行径直向下的对应的那一个位置
在这里插入图片描述

  1. 在linux中我们使用 ‘\r’ 表示回车,使用 ‘\n’ 表示回车换行

2. 缓冲区

  1. 我们在文件中编写一个这样的代码,sleep函数的作用是休眠指定n秒,在这里对应休眠2秒,sleep的头文件是#include <unistd.h>
    在这里插入图片描述

  2. 使用gcc进行编译链接为可执行程序之后,运行,我们知道c语言程序的执行是从上往下开始进行执行的,那么小编调用printf打印在屏幕上的hello去哪了呢,为什么既然是从上往下逐语句执行为什么不显示hello呢?

在这里插入图片描述

  1. 2秒后,我们的hello被打印在了屏幕上,这究竟是为什么呢?首先我们的hello最开始没有打印出来,2秒后被打印出来了,那么说明这个hello字符串一定没有丢失,一定被保存起来了,这个保存的位置就叫做缓冲区,这个缓冲区是由c语言维护的一段内存,当程序结束或遇到’\n’的时候会进行刷新缓冲区的内容,这时候当执行完sleep休眠2秒后程序结束,缓冲区的内容自然也就被刷新出来显示在屏幕上

在这里插入图片描述

  1. 那么我们有没有办法可以强制刷新缓存区的内容呢?有的,可以使用fflush函数强制刷新缓冲区的内容,fflush函数需要传参对应的流,流分为标准输入流(stdin),标准输出流(stdout),标准错误流
  2. 由于是将hello刷新输出到我们的屏幕上,所以需要传入标准输出流(stdout)即 stdout 作为fflush的参数

在这里插入图片描述

  1. 那么我们对应修改一下我们的代码,调用fflush强制刷新缓冲区即可
    在这里插入图片描述
  2. 使用gcc进行编译链接为可执行程序之后,运行之后由于c语言程序是逐语句进行执行,hello被存储在了缓冲区,接着fflush函数会立即刷新缓冲区,那么缓冲区的内容hello就被刷新出来并立即显示到了屏幕上
    在这里插入图片描述
  3. 由于输出hello之后,还有sleep需要休眠2秒,所以2秒之后程序结束
    在这里插入图片描述

二、倒计时小程序

先看效果
在这里插入图片描述

1. 实现

  1. 我们默认生成10秒的倒计时,这个可以自行修改,取决于自己
  2. sleep的头文件是#include <unistd.h>
  3. 那么实现原理很简单,使用一个while循环即可,那么需要注意使用fflush刷新缓冲区,并且使用sleep每次打印后休眠1秒,这样才有一个倒计时的效果
  4. 同时10其实在电脑上的打印是字符’1’和字符’0’,如果不加格式控制我们使用回车打印9之后在屏幕上显示就变成了字符’9’和字符’0’,所以我们需要进行格式控制,这里的格式控制使用%2d,即默认占两个字符(10就是两个字符),如果实际内容为1个字符(从0到9都是1个字符),那么另一个字符位置不显示内容,这样生成之后,由于默认是右对齐,所以在屏幕上打印字符’9’,就变成了空格字符’9’,不符合我们的阅读习惯,所以我们修改一下默认对齐方式修改成左对齐即%-2d即可
  5. 最后为了我们的0在经过回车之后,在程序结束之后,bash命令行要进行显示,那么就会把我们的0给覆盖,为了0不被覆盖,我们人为加一个换行即可
#include <stdio.h>
#include <unistd.h>

int main()
{
	int cnt=10;
	
	while(cnt>=0)
	{
		printf("%-2d\r",cnt);
		fflush(stdout);
		
		cnt--;
		
		sleep(1);
	}
	
	printf("\n");
	
	return 0;
 }  

三、进度条小程序

先看效果
在这里插入图片描述

1. 基本框架

进度条的英文是progressbar,这里给文件起名要对应,我们建立两个源文件progressBar.c文件用于编写进度条函数的实现,main.c文件用于编写进度条函数和基本框架逻辑,一个头文件用于编写头文件和函数声明和宏定义
同时为了便于使用make/makefile的自动化构建,我们还要创建一个makefile文件用于配置编译和清理工作
在这里插入图片描述

2. 实现细节

1. makefile

【linux】linux项目自动化构建工具-make/Makefile—学习make/makefile详情请点击<—,根据此文章,小编对于makefile的配置只讲解重点

  1. 关于makefile配置如下,我们编译main.c和progressBar.c并链接即可,这里我们没有加入头文件,因为头文件已经包含在了源文件中,进行预处理的时候会在源文件进行展开
progressBar:main.c progressBar.c
	gcc $^ -o $@

.PHONE:clean
clean:
	rm -f progressBar
2. progressBar.h
  1. #pragma once是条件编译,防止头文件被重复展开
  2. 要使用到memset函数其对应的头文件是#include <string.h>,要使用到usleep函数其对应的头文件是#include <unistd.h>,这里的usleep的单位是微秒,我们可以更为精细的控制程序执行完成的总时间,1秒等于1000毫秒,1毫秒等于1000微秒,所以1秒等于1000000微秒,我们在调用进度条函数的时候要传入每次打印的时间,一般是设置为50000微秒,程序总执行100次,那么就是5000000微秒,即程序被我们设定为了5秒跑完
  3. 进度条的头’>‘和身体’-'这里我们将其设置为宏,便于修改
  4. 函数声明前也可不加extern,但是全局变量的声明必须加extren
#pragma once                                                                                                                                             

#include <stdio.h>
#include <unistd.h>
#include <string.h>

#define BODY '-'
#define RIGHT '>'

extern void progressbar(int speed);
3. main.c
  1. main.c文件中包含着调用进度条函数,是整个程序的基本框架的主逻辑,这里的调用参数为什么设置为50000的原因在progressBar.h中有讲,具体原因请看上文
#include "progressBar.h"

int main()
{
	progressbar(50000);                                                                                                                                    

	return 0;
}
4. progressBar.c

在这里插入图片描述

  1. 我们要实现的是一个带有进度条并且带有百分之多少的进度并且带有一个类似于加载旋转的三个功能的程序,我们使用[ ]进行分隔这三个功能
  2. 进度条我们可以使用字符数组来进行模拟,从0个字符到100个字符,并且打印我们使用printf打印字符串%s的打印方式,遇到’\0’打印字符串才会停止,由于我们要展示100个字符,在第100个字符的后面我们还应该放一个’\0’,用于终止字符串的打印,所以我们的数组中要先开101个空间并且使用memset函数全部初始化为’\0’,但是实际上要开102个空间,具体原理请继续往下阅读
  3. 我们的过程由于是要向屏幕上打印100个’-'用于表示进度加载完成,最开始为0个,那么就有101次循环,我们定义一个变量为0,当它小于等于100的时候执行代码逻辑,那么这样就可以实现出有101次循环,那么接下来小编带你们模拟一下过程,并且证明字符数组要开102个空间,程序才能够正常运行

在这里插入图片描述

  1. 我们的模拟加载旋转的字符这里就采用|/-\进行模拟,其中\是特殊的转义字符,我们要使用双\\才能进行显示,所以这里的lable指针指向的字符串虽然小编使用了五个字符,但是实际上表示完之后是4个字符,我们要进行根据cnt的变化来对应完成对字符|/-\的按顺序打印,那么我们可以使用strlen函数(strlen是专门求字符串的长度,遇到’\0’才会停止,求的是’\0’之前的字符个数)求出label指针指向的|/-\的字符个数len,那么接下来我们使用cnt%len,那么不就会随着cnt的++,进而逐个产生0,1,2,3之间的数字吗,我们使用label[cnt%len],不就正好可以去模拟加载旋转的字符了
  2. 剩下的细节注意好每次输出字符串的时候由于没有’\n’的存在,缓冲区不会被刷新,使用fflush强制刷新缓冲区
  3. 注意好使用usleep在每次刷新完缓冲区将字符串显示在屏幕上之后进行休眠50000微秒即可
  4. 最后为了我们的进度条在经过回车之后,在程序结束之后,bash命令行要进行显示,那么就会把我们的进度条给覆盖,为了进度条不被覆盖,我们人为加一个换行即可
  5. printf(“[%-100s][%d%%][%c]\r”,str,cnt,lable[cnt%len]),进行打印编写的时候一定要注意将\r放在最后面,\r会默认将第一个%d输出的位置对齐屏幕的起始位置,但是这里的起始位置由于我们放置了[,所以对齐到[的后面,只有这样才可以正常执行,并且由于%也是一个特殊符号我们输入一个%无法将其正常显示在屏幕上,那么我们需要输入两个%%才可以在屏幕上显示一个%,有人好奇,小编小编,这里你为什么不使用转义字符’\‘呢,因为这个转义字符的使用在linux中可能会出现报错,并且在linux中在屏幕上显示一个特殊字符%主流的写法就是使用两个%%进行编写,同时例如在下载应用的时候想一下进度条通常都是有预留空间的,由于我们的最终是想要在屏幕上打印100个字符’-'作为进度条的最终结束状态,所以这里我们使用[%100s]的方式进行在屏幕上占位100个字符,并且使用[ ]的中间作为我们输出进度条的空间,同时由于占位之后编译器默认是右对齐,不符合我们的阅读习惯,所以我们修改一下默认对齐方式修改成左对齐即[%-100s]即可

完成在这里插入图片描述

#include "progressBar.h"

void progressbar(int speed)
{
	const char* lable="|/-\\";
	char str[102];
	memset(str,'\0',sizeof(str));
	
	int len=strlen(lable);
	int cnt=0;
	
	while(cnt<=100)
	{
		printf("[%-100s][%d%%][%c]\r",str,cnt,lable[cnt%len]);
		fflush(stdout);
	
		str[cnt++]=BODY;
	
		if(cnt<100)
		{
			str[cnt]=RIGHT;
		}                                                                                                                                                    
	
		usleep(speed);
	}
	
	printf("\n");
}

四、进度条小程序plus升级版本

先看效果
在这里插入图片描述
其实这个升级版本的主要作用是接收一个从0开始(注意必须是从0开始,因为字符数组要逐个从0开始使用字符去初始化到100个字符)到100的参数,进而直接给出对应进度条状态,将休眠和循环的设计工作交给了用户来完成可以更为符合用户需求,主要可以用于模拟下载场景,并且使用了回调函数的特性,并且注意当存在连续多次下载的工作的时候要提前对字符数组清空,因为上一次已经将字符数组进行填满了,如果不加清空直接使用,那么最初进度条的状态就已经是满了,具体小编不再展开讲,感兴趣的铁铁们的可以看一下代码实现

1. makefile
progressBar:main.c progressBar.c
		gcc $^ -o $@

.PHONE:clean
clean:
	rm -f progressBar
2. progressBar.h
#pragma once

#include <stdio.h>
#include <unistd.h>
#include <string.h>

#define BODY '-'
#define RIGHT '>'

extern void progressbar(int cnt);
extern void initbar(char* str,int sz);

char str[102];
3. main.c
#include "progressBar.h"

typedef void (*callback_t)(int);

void downLoad(callback_t cb)
{
  int total=1000;
  int cur=0;

  while(cur<=total)
  {
    int rate=cur*100/total;
    
    cb(rate);
    usleep(50000);
    cur+=10;
  }

  printf("\n");
}

int main()
{

  printf("downLoad\n");
  initbar(str,sizeof(str));
  downLoad(progressbar);
  
  printf("downLoad\n");
  initbar(str,sizeof(str));
  downLoad(progressbar);
  
  printf("downLoad\n");
  initbar(str,sizeof(str));
  downLoad(progressbar);

  return 0;
}
4. progressBar.c
#include "progressBar.h"                                                                                                                                 

const char* lable="|/-\\";
void progressbar(int cnt)
{
	if(cnt<0||cnt>100)
	{
		return;
	}

	int len=strlen(lable);
	printf("[%-100s][%d%%][%c]\r",str,cnt,lable[cnt%len]);
	fflush(stdout);
	
	str[cnt++]=BODY;
	
	if(cnt<100)
	{
		str[cnt]=RIGHT;
	}
}

void initbar(char* str,int sz)
{
	memset(str,'\0',sz);
}

总结

以上就是今天的博客内容啦,希望对读者朋友们有帮助
水滴石穿,坚持就是胜利,读者朋友们可以点个关注
点赞收藏加关注,找到小编不迷路!