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 }

编译运行:

相关推荐
大树881 小时前
金刚石散热越强,管路越先见顶
大数据·运维·服务器·人工智能·ai
摇滚侠1 小时前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
霸道流氓气质2 小时前
领域驱动设计(DDD)在 Spring Boot 微服务中的实践指南
运维·spring boot·微服务
bush42 小时前
嵌入式linux学习记录十四、术语
linux·嵌入式
载数而行5202 小时前
Linux 11 动态监控指令top
linux
小宇宙Zz2 小时前
Maven依赖冲突
java·服务器·maven
Inhand陈工3 小时前
基于台达PLC与映翰通IG502的智慧水产养殖精准投喂与远程运维解决方案
运维·人工智能·物联网·阿里云·信息与通信
酣大智3 小时前
ARP代理--工作原理
运维·网络·arp·arp代理
不会C语言的男孩3 小时前
Linux 系统编程 · 第 8 章:进程基础
linux·c语言
shushangyun_4 小时前
2026年快消品B2B系统推荐:支持终端门店订货、促销政策自动化的工具?
java·运维·网络·数据库·人工智能·spring·自动化