用C语言实现第一个小程序——“进度条”

本文使用到的工具有gcc vim make/makefile,边学习边实践,快速上手!
93344959626748e7d01134674777d512_MD5

b16c30dcfb1b6cdc40928a0acab97066_MD5

ddacadc4dcf8cb40cf2c841eb795998e_MD5
@TOC

【Linux】用C语言实现第一个小程序——“进度条”

首先我们补充两个小知识

1. 两个补充

1.1).makefile文件编写

先写通makefile,防止源文件被覆盖或删除
dfefb5d6ebaf5c97ea41f31736dd6f9c_MD5

1
2
3
4
5
processbar:main.c
gcc -o $@ $^
.PHONY:clean
clean:
rm -f processbar

![[Pasted image 20231107174117.png]]
fae7e30e331b4e55148e190b239420a7_MD5
生成可执行文件,第一步makefile文件编写完成。

1.2). 介绍休眠函数

sleep()

函数库文件<unistd.h>
sleep(3) 程序休眠3秒后退出
unistd.hunix std的意思,包含了许多UNIX系统服务的函数原型。

现象

1)加‘\n’后会再显示器显示,光标闪动3秒后停止:
5e12843ffe098af63b7363cdacbba208_MD5

2)不加’\n’ 系统会休眠3秒,之后再显示器显示:
2cb6368a3e56340db4c8dc9863000541_MD5

以上现象体现了c语言提供的缓冲区,2)在休眠时候就已经结束了,c语言是顺序结构,从上往下按顺序执行。输出的字符串被保存起来,因为c语言会为io函数提供一块【a.缓冲区】,在退出时向屏幕输出。
b.【 回车与换行
我们平时写满一行时按enter来转向下一行,其实是两个动作,回车+换行。但事实上回车!=换行。换行指的是到当前列的下一行;而回车指的是:回到当前位置的开头位置
这也解释了为什么以往的计算机键盘enter占用了两个位置。
260fd778514b94fec666799fb74df773_MD5

所以在c语言中,有以下两个命令:
\r只回车不换行,控制光标回到该行开始等待输入;
\n回车+换行,控制光标回带下一行开头位置等待输入;

fflush(stdout):将缓冲区中内容刷新到显示器中;
usleep(); 以微秒时间进行休眠,可用man usleep来查看相关介绍

1.3倒计时

我们先小试牛刀,用刚才所学知识来写一个倒计时

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include<stdio.h>    
#include<unistd.h>
int main()
{
//printf("Are you ok?\r");
int cnt=10;
while(cnt>=0)
{
printf("%-2d\r",cnt); //预留2个字符大小的位置,负号表示左对齐
cnt--;
fflush(stdout); //将缓冲区中内容刷新到显示器中
sleep(1);
}
printf("\n");
return 0;
}

2. 文件创建+链接

首先先创建所需要的文件:
①:makefile ②:processbar.h
③:processbar.c ④:main.c

配置makefile文件;

1
2
3
4
5
6
7
8
processbar:main.o processbar.o    
gcc -o $@ $^
main.o:main.c //.o依赖.c ……
gcc -c main.c
processbar.o:processbar.c gcc -c processbar.c //-c 直接生成同名.c文件
.PHONY:clean
clean:
rm -f main.o processbar.o processbar

makefile配置成功!
09cb6b8aa11f2a87bd3aefcef8ab0774_MD5
注:
-c:直接生成同名文件,不用生成入.o .i的中间文件
进度条可以看做一个不断递增的字符串,多一个`\n,

30625057136ef90d6e6f0ba5c4835185_MD5

b460cc4a3beb4c4dcb73f71e460f5fe1_MD5


3. Vision 1. 入门版

Vision 1的版本由进度条+比率+旋转光标构成

3.0 代码

1
2
3
4
5
6
7
8
9
10
11
#pragma once    //防止头文件被重复包含    
#include<stdio.h>
#include<string.h>
#include<unistd.h>

#define MAX 101
#define Body '='
#define Head '>'

//vision 1
void process2();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include"processbar.h"    

//vision 1
const char*lable="|/-\\";
void process1()
{
// const int n = 0;
// char buffer[n]; //C99前的标准不支持变长数组
char buffer[MAX];
memset(buffer, '\0', MAX); //初始化数组,memset头文件string.h
int cent=0;
int n = strlen(lable);
buffer[0] = Head;
while(cent<MAX)
{
printf("[%-100s][%d%%][%c]\r",buffer,cent,lable[cent%n]);
//向左预留空间,左对齐 %%打印百分位 -r 只回车不换行 [%d%%],cent ——>比率
buffer[cent++] = Body; //后置++是后一个为Body-==
buffer[cent] = Head;
if(cent==MAX-1) buffer[cent] = Body;
usleep(50000); //0.1s睡眠一次,100000毫秒
fflush(stdout); //刷新缓冲区
}
printf("\n");
}

93344959626748e7d01134674777d512_MD5
![[Pasted image 20231111214707.png]]

4. Vision 2

当前进度条是由自己控制的,但进度是多少?如何将进度条数据与实际情况结合起来呢?依附于某种场景,比如应用下载:

4.1 download场景模拟

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include"processbar.h"
#include<time.h>
#include<stdlib.h>
#include<unistd.h>

#define FILESIZE 1024*1024*1024 //~1G

void download()
{
srand(time(NULL)^1023);
int total = FILESIZE;
while(total)
{
usleep(10000);
int one = rand()%(1024*1024*10); //下载速度->10M
total -= one; if(total < 0) total = 0; //当前进度 int download = (FILESIZE) - total;
double rate =(download*1.0 / (FILESIZE))*100; //显示整数
printf("rate:%f\n",rate); } }

模拟成功~~~
ceead6df8712232a7c83633a806a7390_MD5

4.2 结合应用案例(download)

1
2
3
4
5
6
7
8
9
10
11
//头文件
#pragma once //防止头文件被重复包含
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#define MAX 101
#define Body '=' #define Head '>'
//vision 1
void process2();
//version 2
void process2(double rate);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const char*lable="|/-\\";
char buffer[MAX] ={0}; //循环由download控制,所以把Body的数组定义为全局变量
//vision 2
void process2(double rate)
{
static int cent=0; //控制旋转图标
int n = strlen(lable);
if(rate<=1.0) buffer[0] = Head; //小于1时为'='
printf("[\033[4;32;44m%-100s\033[0m][%.1f%%][%c]\[%c]\r",buffer,rate,lable[cent%n]);
//带颜色的打印,说明见下
//下载百分比由rate控制
fflush(stdout); //刷新缓冲区
buffer[(int)rate] = Body; //后置++是后一个为Body-==
if((int)rate< MAX-2) buffer[(int)rate+1] = Head; //头为'>',结果相当于前三个位置部位'>',('\0',最后一个为'=',在之后语句中控制的rate+1(为了控制'>'在'='前面),所以会多)
if(rate>=100.0) printf("\n");
cent++; //控制光标旋转
}

4.3 字符颜色

1
printf("[\033[4;32;44m%-100s\033[0m][%.1f%%][%c]\[%c]\r",buffer,rate,lable[cent%n]);

开始设置属性:\033[31m—> \033[31m+背景色+字体色+显示效果
关闭所有属性:\033[0m
背景色(40-47):设置背景色【40: 黑 41: 红 42: 绿 43: 黄 44: 蓝 45: 紫 46: 深绿 47: 白色】
字体色(30-37):【30: 黑 31: 红 32: 绿 33: 黄 34: 蓝 35: 紫 36: 深绿 37: 白色】

ddacadc4dcf8cb40cf2c841eb795998e_MD5

5. 文章源代码

  • makefile文件
1
2
3
4
5
6
7
8
9
processbar:main.o processbar.o
gcc -o $@ $^
main.o:main.c
gcc -c main.c
processbar.o:processbar.c
gcc -c processbar.c
.PHONY:clean
clean:
rm -f processbar main.o processbar.o
  • 头文件
1
2
3
4
5
6
7
8
9
10
11
12
#pragma once    //防止头文件被重复包含
#include<stdio.h>
#include<string.h>
#include<unistd.h>

#define MAX 101
#define Body '='
#define Head '>'

//vision 1
void process2();
void process2(double rate);
  • processbar.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include"processbar.h"

//vision 1
const char*lable="|/-\\";
void process1()
{
// const int n = 0;
// char buffer[n]; //C99前的标准不支持变长数组
char buffer[MAX];
memset(buffer, '\0', MAX); //初始化数组,memset头文件string.h
int cent=0;
int n = strlen(lable);
buffer[0] = Head;
while(cent<MAX)
{
printf("[%-100s[ %.3f%%][%c]\r",buffer,cent,lable[cent%n]);
//向左预留空间,左对齐 %%打印百分位 -r 只回车不换行 [%d%%],cent ——>比率
buffer[cent++] = Body; //后置++是后一个为Body-==
buffer[cent] = Head;
if(cent==MAX-1) buffer[cent] = Body;
usleep(50000); //0.1s睡眠一次,100000毫秒
fflush(stdout); //刷新缓冲区
}
printf("\n");
}

char buffer[MAX] ={0}; //循环由download控制,所以把Body的数组定义为全局变量
//vision 2
void process2(double rate)
{
static int cent=0;
int n = strlen(lable);
if(rate<=1.0) buffer[0] = Head;
printf("[\033[4;32;44m%-100s\033[0m][%.1f%%][%c]\r",buffer,rate,lable[cent%n]);
fflush(stdout);
buffer[(int)rate] = Body; //后置++是后一个为Body-==
if((int)rate< MAX-2) buffer[(int)rate+1] = Head;
if(rate>=100.0) printf("\n");
cent++;
}
  • main.c 测试文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#define FILESIZE 1024*1024*1024 //~1G

void download()
{
srand(time(NULL)^1023);
int total = FILESIZE;
while(total)
{
usleep(10000);
int one = rand()%(1024*1024*5); //下载速度->10M
total -= one;
if(total < 0) total = 0;
//当前进度
int download = (FILESIZE) - total;
double rate =(download*1.0 / (FILESIZE))*100; //显示整数
process2(rate);
}
}

int main()
{
//vision 1
//process1();
download();
return 0;
}
👀👀