Linux:Linux中第一个小程序_进度条

前言:

在日常生活中,我们下载软件,文件,都会都一个进度显示,来告知我们的下载进度,接下来我们可以自己手搓一个进度条,在我们自己写扫雷、贪吃蛇等小游戏时,可以做一个游戏加载进度使用

在手搓进度条之前,我们需要一些知识作为铺垫,这些知识大家或多说少会有了解,但是我为了让大家更好的了解,我会把这些知识再细讲一下,方便理解。

一、回车换行

回车换行并不是一个相同的概念,回车是回车,换行是换行,回车换行是两个概念

假设下面两行是我们的一张纸,我们一行只能写四个字,写完之后,光标(红色竖线)停留在第四格内,一般都是直接按回车换行使光标切换到第二行的第一个位置

1、回车概念

但是如果我们单独回车,光标就会回到第一行的开始

这就是回车

2、换行概念

如果我们单独换行,光标所在列位置不变,直接切换到下一行

这就是换行


所以说我们**回车换行,**可以说是先回到最左边的位置,再往下跳一行,也可以说是先往下跳一行,再回到最左边的位置

3、相关历史

1、老式键盘

老式键盘就很明确,这个形状就像是先换行再回车

2、老式打字机

在老式打字机的使用时,把纸张放上去,打完一行字的时候,拨动滚轮纸张会往上走完成换行,然后再向左拨动针尖位置完成回车

二、缓冲区

我们对缓冲区这个名字应该已经很熟悉了,或多或少都都有了解,我在帮助大家回忆一下

我们几段代码来演示缓冲区的存在:

1、printf函数里带\n

cpp 复制代码
#include <stdio.h>
#include <unistd.h>
int main()
{
 printf("hello Makefile!\n");
 sleep(3);
 return 0;
}

编译运行后我们查看现象:

现象是先打印出来Hello World,之后等三秒才会退出程序

2、printf函数里不带\n

cpp 复制代码
#include <stdio.h>
#include <unistd.h>
int main()
{
 printf("hello Makefile!");
 sleep(3);
 return 0;

现象是前三秒没有打印

三秒后打印出来

毫无疑问的是,我们的代码肯定是先执行printf再执行sleep的,之所以打印的东西在sleep后出现的原因就是缓冲区的存在

我们sleep的三秒中,我们所打印的东西被保存在缓冲区里:

一开始我们要显示的内容会拷贝一份放在缓冲区里,之后再定期刷新,显示到我们的显示器上

程序结束时,会强制刷新缓冲区,所以我们的Hello World在程序结束时会显示到我们的屏幕上


那为什么我们带上\n就会在sleep之前从缓冲区里刷新到显示器上呢?

刷新缓冲区还有其它三种方式

3、刷新缓冲区

我们在这里只讨论显示器的缓冲区刷新

1、\n

碰到\n立即自动刷新

2、缓冲区满

缓冲区满了自动刷新

3、强制刷新

认识一个新函数,fflush()

flush就是刷新,他的意思就是刷新特定的输出流

演示:

我在这段代码里用fflush强制刷新,我们会发现,即使printf不带\n,我们要打印的东西依然会在sleep之前显示到我们的屏幕上。

三、倒计时

我们了解了回车换行与缓冲区的概念,那我们在写进度条前尝试写一个倒计时出来

我们想要的倒计时模样:

倒计时:


代码实现:

cpp 复制代码
#include<stdio.h>
#include<unistd.h>
int main()
{
  int i=10;
  for(i=10;i>=0;i--)
  {
    printf("倒计时:%2d\r",i);
    fflush(stdout);
    sleep(1);
  }
    printf("\n");
    return 0;
}

我们在打印时,每次打印完一个数字,\r都会使光标回到最开始的位置,然后循环再打印会将原来的覆盖

视频演示:

倒计时演示

四、进度条

做完倒计时,我们开始进入手搓进度条,我们为什么要先做倒计时呢,因为我们要做的进度条,原理和倒计时类似,都是不断地覆盖原来的数据。

我们根据这个GIF,来思考一下我们应该如何制作

1、倒计时模型

大致模型:

2、创建文件

我们先创建三个文件,头文件,函数实现源文件,测试源文件

3、项目自动化构建

首先创建一个makefile文件,进入该文件并写入以下代码

这样我们在编译时一个make命令就可以完成了

4、代码实现

1、初版模型实现

1)Processbal.h文件代码
cpp 复制代码
  1 #pragma once
  2 #include<stdio.h>
  3 //void ForTest();
  4 void ProBal();                                                                                                                     
2)Processbal.c文件代码
cpp 复制代码
  1 #include"Processbal.h"
  2 #include<unistd.h>
  3 #include<string.h>
  4 #define Length 101
  5 #define Style '='
  6 const char*lable="|/-\\";
  7 //void ForTest()
  8 //{
  9 //  printf("!!!");
 10 //}
 11 void ProBal()
 12 {
 13   char bar [Length];
 14   memset(bar,'\0',sizeof(bar));
 15   int len=strlen(lable);
 16   int cnt=0;
 17   while(cnt<=100)
 18   {
 19     printf("[%-100s][%-3d%%][%c]\r",bar,cnt,lable[cnt%len]);
 20     fflush(stdout);
 21     bar[cnt++]=Style;                                                                                                              
 22     usleep(50000);
 23   }
 24   printf("\n");
 25 }
3)main.c文件代码
cpp 复制代码
  1 #include"Processbal.h"
  2 int main()
  3 {
  4   ProBal();                                                                                                                        
  5   return 0;
  6 }

编译运行:

但是我们这个显然是不够用的,我们只是弄了一个进度条出来,而现实是我们下载的东西是有大小的,包括我们网速大小,我们下载时有对应的带宽,我们呢接下来让他更切合实际。

2、最终版本

1)Processbal.h文件代码
cpp 复制代码
  1 #pragma once
  2 #include<stdio.h>
  3 //void ForTest();
  //函数指针方便我们把函数作为参数传给另一个函数
  4 typedef void (*callback_t)(double,double);
  5 void ProBal(double total,double current); 
2)Processbal.c文件代码
cpp 复制代码
 1 #include"Processbal.h"
  2 #include<unistd.h>
  3 #include<string.h>
  4 #define Length 101
  5 #define Style '='
  6 const char*lable="|/-\\";
  7 //void ForTest()
  8 //{
  9 //  printf("!!!");
 10 //}
 11 void ProBal(double total,double current)
 12 {
 13   char bar [Length];
 14   memset(bar,'\0',sizeof(bar));
 15   int len=strlen(lable);
 16   int cnt=0;
 //   current的占比,每次推动的百分比
 17   double rate=(current*100.0)/total;
 18   int loop_count=(int)rate;
 19   while(cnt<=loop_count)
 20   {                                                                                                                                
 21     bar[cnt++]=Style;
 22   }
 23   printf("[%-100s][%-3d%%][%c]\r",bar,cnt,lable[cnt%len]);
 24   fflush(stdout);
 25 }
3)main.c文件代码
cpp 复制代码
  1 #include"Processbal.h"
  2 #include<unistd.h>
  3 double bandwidth=1024*1024*1.0;
  4 void download(double filesize,callback_t cb)
  5 {
  6   double current=0.0;
  7   printf("download begin,current:%lf\n",current);
  8   while(current<=filesize)
  9   {
 10     cb(filesize,current);
 11     usleep(100000);
 12     current+=bandwidth;
 13   }
 14   printf("\n");                                                                                                                    
 15   printf("download done,filesize:%lf\n",filesize);
 16 }
 17 int main()
 18 {
 //   我们可以自己传自己的目标数据大小进行下载
 19   download(1024*1024*50*1.0,ProBal);
 20   return 0;
 21 }

编译运行:

相关推荐
Hacker_Oldv4 分钟前
WPS 认证机制
运维·服务器·wps
bitcsljl12 分钟前
Linux 命令行快捷键
linux·运维·服务器
ac.char15 分钟前
在 Ubuntu 下使用 Tauri 打包 EXE 应用
linux·运维·ubuntu
Cachel wood34 分钟前
python round四舍五入和decimal库精确四舍五入
java·linux·前端·数据库·vue.js·python·前端框架
Youkiup42 分钟前
【linux 常用命令】
linux·运维·服务器
qq_297504611 小时前
【解决】Linux更新系统内核后Nvidia-smi has failed...
linux·运维·服务器
_oP_i1 小时前
.NET Core 项目配置到 Jenkins
运维·jenkins·.netcore
weixin_437398211 小时前
Linux扩展——shell编程
linux·运维·服务器·bash
小燚~1 小时前
ubuntu开机进入initramfs状态
linux·运维·ubuntu
小林熬夜学编程1 小时前
【Linux网络编程】第十四弹---构建功能丰富的HTTP服务器:从状态码处理到服务函数扩展
linux·运维·服务器·c语言·网络·c++·http