目录
前言
前面我们已经学会基本的linux指令和,那么现在来搞个好玩的东西-进度条(简单版本)!!!
基本知识
ⅰ、回车和换行
首先得明确,回车和换行对于我们而言就是一个简单的动作,可是对于机器来说这是两个独立的动作。
- 换行
- 回车
键盘上的回车键是先向下再向左的,对应的就是先换行再回车的两个动作!!
在c语言中,\n是表示回车换行同时,\r仅仅是回车。
ⅱ、缓冲区
两个有意思的现象
①有‘\n’
②无‘\n’
对于无‘\n’的现象,我们需要明确一点,程序并不是先休眠了3秒才显示字符串,通俗点来说就是不是先执行sleep函数才去执行printf函数,同样也是先执行printf函数才去执行的sleep函数,因为程序是从上往下依次执行的。那么在这段休眠时间里字符串在哪?被放在了缓冲区里
简单定义
目前我们仅需知道缓冲区实际上就是一块内存空间,对于上面的字符串,就是被保存在这样的一块内存空间里,当程序运行结束时,会自动的刷新缓冲区里的内容到显示器上,所以无‘\n’才能看到上述的现象。
刷新缓冲区
①加上‘\n’,立即刷新
②等待缓冲区满或者程序结束,自动刷新
③强制刷新
怎么强制?
实际上c语言提供了一个的函数--fflush,这个函数就是用于强制刷新缓冲区的。。
这里的文件流后面的文章有详细讲解,现在我们只需要知道程序在运行时默认会打开三种流:
- 标准输入,stdin
- 标准输出,stdout
- 标准错误,stderr
我们的显示器就是标准输出文件,linux下一切皆文件!所以可以采用该函数强制刷新到对应的文件流上。
演示:
有了上面的知识可以先来实现一个简单的倒计时程序
简易倒计时程序
原理:实际就是对同一个位置进行覆盖,进而实现一个动态的效果。
每当显示一个数字之后就将光标挪动至最前面(回车),休眠,然后再显示下一个数字。回车配休眠需要强制刷新缓冲区。
代码:
1 #include <stdio.h>
2 #include <unistd.h>
3
4 int main()
5 {
6 int cnt=10;
7
8 while(cnt>=0)
9 {
10 printf("倒计时:%2d\r",cnt);
11
12 fflush(stdout);//强制刷新到屏幕
13 sleep(1);
14 cnt--;
15 }
16 return 0;
17 }
注意:为什么用%2d,是因为数字显示在屏幕上时,实际上是字符串的形式,相当于10是两个字符的字符串,需要覆盖两个字符。当然,大家感兴趣把2去掉看看!
结果如下:
进度条代码
这里采用多文件的形式去实现的,为了更加方便的去管理代码。
多文件下makefile写法
一代(无任何场景)
procs1.h代码
主要是头文件的包含以及函数的声明!
#pragma once
#include <stdio.h>
#include <string.h>
#include <unistd.h>
void processbar1();
procs1.c代码
主功能实现
代码如下:
#include "procs.h"
#define length 101 //进度条长度
#define style '|' //进度条样式
const char* lab="|/-\\";//加载旋转光标
void processbar1()
{
char bar[length];//字符数组
memset(bar,'\0',sizeof(bar));//初始化为\0
int cnt=0;
while(cnt<=100)
{
printf("[%-100s][%3d%%][%c]\r",bar,cnt,lab[cnt%strlen(lab)]);//\r为了完成覆盖
fflush(stdout); //强制刷新
bar[cnt++]=style;//字符追加至数组中
usleep(100000);
}
printf("\n");
}
主函数main1.c
调用函数即可。
一代运行结果:
二代 (搭配下载场景)
进度条不可能单独存在,一般常见的场景就是下载这一场景,会根据网络带宽,文件大小等其他的要素来决定下载进度,当然哦这里只是简单的模拟一下,没有涉及从网络获取数据等其他相关知识。
procs2.c代码
这里的改进主要是函数头包含了文件的总大小以及当前的下载进度。根据进度打印进度条!
#include "procs.h"
#define length 101 //进度条长度
#define style '|' //进度条样式
const char* lab="|/-\\";//加载旋转标志
void processbar2(double total,double current)
{
char bar[length];
memset(bar,'\0',sizeof(bar));//初始化为\0
int len=strlen(lab);
int cnt=0;
double rate=(current*100.0)/total;//计算比率
int load_top=(int)rate;
while(cnt<=load_top)
{
bar[cnt++]=style;//字符追加至数组中
}
printf("[%-100s][%.1lf%%][%c]\r",bar,rate,lab[cnt%len]);//\r为了完成覆盖
fflush(stdout); //强制刷新
}
procs2.h代码
#pragma once
#include <stdio.h>
#include <string.h>
#include <unistd.h>
typedef void(*call_back)(double,double);//定义函数指针类型,为了方便回调
void processbar2(double total,double current);
注意:这里声明了一个call_back函数指针类型,目的就是实现函数回调。因为未来可能还有更多版本的进度条功能代码, 采用函数指针的方式,只需要传入对应的函数名,就会去调用对应的功能代码。
主函数main2.c 代码
#include "procs.h"
double bandwith=1024*1024*1.0;//下载速度1mb/s
void download(double filesize,call_back cb)//函数指针做参数
{
double cnt=0.0;
printf("download begin....,bandwith is:%.1lf\n",bandwith);
while(cnt<=filesize)
{
cb(filesize,cnt);
usleep(100000);
cnt+=bandwith;
}
printf("\ndownload finishi......,filesize is:%.1lf\n",filesize);
printf("\n");
}
int main()
{
download(50.0*1024*1024,processbar2);//50mb
download(10.0*1024*1024,processbar2);//10mb
download(100.0*1024*1024,processbar2);//100mb
return 0;
}
如代码,下载功能存在一个函数指针类型的参数,只需传入函数名(函数地址),就能找到对应函数的功能实现,效率极高也十分的巧妙。
二代结果显示:
可以看到,进度条会根据文件大小的不同,下载的速度也不一样,同一带宽情况下,文件越大,那必然越慢,如上图的10mb比100mb快多了。。
二代的场景更加贴近实际哦!!
好了,今天的分享就到这里,如果对你有帮助,欢迎三连!!!
发表评论