Liunx 小程序之进度条

Liunx 小程序之进度条

效果

先来看效果,这其实是一个动态的进度条,后有源码,运行即可:

前提条件

在制作之前有两个前提条件需要了解

回车和换行

这个概念,一般人均会混为一谈,而程序员因为熟知 '\n' ,所以深刻理解 换行符 是 将当前光标位置换到下一行的开头

'\n' 这个字符其实是两个动作,一个是 回车 ,一个是 换行

  • 换行 :顾名思义,只是将当前光标位置换进下一行,但是其在下一行的什么位置,取决于之前上一行的所在位置
  • 回车 :这是 将光标置于此行开头位置

所以一般来说, 换行符 所完成的是这 两个动作 的总和

而在 C 语言里,只 回车'\r' ,所以有些人会写出 "\r\n" 这样的代码,这时候的 '\n' 就只表示 换行

缓冲区

有了上面 回车换行 的解释,对于 缓冲区 ,我们需要对比观察这样两份代码:

c 复制代码
// 第一份代码:
#include <stdio.h>
#include <unidstd.h>
int main()
{
	printf("Hello World...\n");
	sleep(3);
	return 0;
}
c 复制代码
// 第二份代码:
#include <stdio.h>
#include <unidstd.h>
int main()
{
	printf("Hello World...");
	sleep(3);
	return 0;
}

不同之处 仅在于代码 printf("Hello World..."); 打印的字符串是否含有 '\n' 换行符

那么请将这两份代码放在命令行终端进行验证,分别编译运行后,眼睛看到的结果将不一样:

  1. 第一份会将 Hello World... 打印出来并空出一行,休眠 3 秒
  2. 第二份会先休眠 3 秒,再打印出 Hello World... 这时由于没有 换行符 ,下一个 命令行提示符 会紧跟字符串打印出来

请注意,这是肉眼观察到的结果,那为什么第二份代码肉眼观察是先休眠呢?

首先我们知道,C 语言对上面的两份代码一定是 顺序执行 的,也就是说 均为先打印,后休眠 ,既如此,那第二份代码的字符串为何等休眠后才出现呢?真正的字符串又被打印在了哪里?

首先,缓冲区可以看作是内存里的一块空间; printf 函数也并不是把字符串直接显示在显示器屏幕上,而是将字符串拷贝进这块缓冲区,再由缓冲区刷新到显示器屏幕上

第二段程序由于没有 '\n',会将字符串一直放在缓冲区里,直到程序结束,自动冲涮缓冲区时才被刷新到屏幕上

目前来说 缓冲区的刷新策略是:

  1. 含有 '\n' ,会将 '\n' 前面所有的内容都刷新出来(行刷新)
  2. 缓冲区满即刷新
  3. 使用函数 fflush(stdout); 强制刷新

倒计时

有了上面的解释,咱就可以先着手写一个 倒计时 ,代码如下:

c 复制代码
// pre-test.c 源文件
#include <stdio.h>
#include <unistd.h>
int main()
{
    for (int i = 10; i >= 0; --i)
    {
        printf("倒计时:%2d\r", i);
        fflush(stdout);
        sleep(1);
    }
    printf("\n");
    return 0;
}
bash 复制代码
# makefile 文件
bin=pre-test
src=pre-test.c

$(bin):$(src)
	gcc $^ -o $@ -std=c99

.PHONY:clean
clean:
	rm -f $(bin)

试一试咯 ^ ^

进度条

纯进度条

我们先完成一个 纯进度条 的代码:

c 复制代码
#define Length 101
#define Style '#'
const char* lable = "|/-\\";

void ProsBar()
{
    char bar[Length];
    memset(bar, '\0', sizeof(bar));
    int cnt = 0;
    int len = strlen(lable);
    while (cnt <= 100)
    {
        printf("[%-100s][%3d%%][%c]\r", bar, cnt, lable[cnt % len]);
        fflush(stdout);
        bar[cnt++] = Style;
        usleep(20000);
    }
    printf("\n");
}

如果能看懂前面 倒计时 的代码,那这里的代码也就可以看懂了,原理都一样,如果运行你会发现,这是个自主加载的进度条,不表示任何事物的进度,那怎么行,接下来我们模拟 下载的进度条

模拟下载的进度条

只要将 纯进度条 看懂,那接下来的代码也一定可以看懂,其实原理和 倒计时 一样,代码不做解释

下面我将把代码写成结构化形式,利用 make 自动化构建

Progressbar.h

c 复制代码
#pragma once 

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

// 定义函数指针,用于回调函数
typedef void(* callback_t)(double, double);

void ProsBar(double total, double current);

Progressbar.c

c 复制代码
#include "Progressbar.h" 
#include <string.h> 

#define Length 101
#define Style '#'

const char* lable = "|/-\\";

void ProsBar(double total, double current)
{
    static char bar[Length] = { 0 };
    // memset(bar, '\0', sizeof(bar));

    static int cnt = 0;
    int len = strlen(lable);
    double rate = (current * 100.00) / total;
    int loop_count = (int)rate;
    while (cnt <= loop_count)
    {
        bar[cnt++] = Style;
        // usleep(10000);
    }
    printf("[%-100s][%.2lf%%][%c]\r", bar + 1, rate, lable[(cnt - 1) % len]);
    fflush(stdout);

    if (cnt >= 100)
    {
        cnt = 0;
        memset(bar, '\0', sizeof(bar));
    }
}

main.c

c 复制代码
#include "Progressbar.h" 

void Download_Simulation(callback_t cb)
{
    double FileSize = 100 * 1.0;  // 文件大小
    double Current = 0.0;         // 下载进度
    double BandWidth = 1.0;       // 网络带宽

    printf("Download start!\n");
    while (Current <= FileSize)
    {
        cb(FileSize, Current);
        Current += BandWidth;
        usleep(50000);
    }
    printf("\nThe file size is %.2lf MB\nDownload complete!\n", FileSize);
}

int main()
{
	// 回调函数
    Download_Simulation(ProsBar);
    return 0;
}

makefile

bash 复制代码
bin=Progressbar 
src=main.c Progressbar.c

$(bin):$(src)
	gcc $^ -o $@

.PHONY:clean
clean:
	rm -f $(bin)
相关推荐
qq_3170609510 分钟前
java之http client工具类
java·开发语言·http
robot_大菜鸟18 分钟前
python_openCV_计算图片中的区域的黑色比例
开发语言·python·opencv
Pandaconda44 分钟前
【C++ 面试 - 新特性】每日 3 题(六)
开发语言·c++·经验分享·笔记·后端·面试·职场和发展
chanTwo_001 小时前
go--知识点
开发语言·后端·golang
悟空丶1231 小时前
go基础知识归纳总结
开发语言·后端·golang
北南京海1 小时前
【C++入门(5)】类和对象(初始类、默认成员函数)
开发语言·数据结构·c++
别挡1 小时前
CentOS Stream 8中安装和使用 Docker
linux·docker·centos
Mr_Xuhhh1 小时前
C语言深度剖析--不定期更新的第六弹
c语言·开发语言·数据结构·算法
No regret.1 小时前
JAVA基础:值传递和址传递
java·开发语言
Yusei_05231 小时前
C++基础知识6 vector
开发语言·c++