当前位置:   article > 正文

【linux】Linux第一个小程序-进度条

【linux】Linux第一个小程序-进度条

1. 预备知识:回车和换行

回车(Carriage Return,CR):

  • 在早期的机械打字机中,回车指的是将打字机的打印头移回到行首的操作,这样打印头就可以开始新的一行的打印。
  • 在ASCII编码中,回车用控制字符CR表示,其编码为\r(即十进制的13)。

换行(Line Feed,LF):

  • 换行是指将打印头向下移动到下一行的操作。
  • 在ASCII编码中,换行用控制字符LF表示,其编码为\n(即十进制的10)。

即:回车,回到当前行的行首,不会切换到下一行,如果接着输出的话,本行以前的内容会被逐一覆盖;
换行,换到当前位置的下一行,而不会回到行首。

在Windows系统中,文本文件的换行符通常是回车加换行(CR+LF,即\r\n)。而在Unix/Linux系统中,换行符仅仅是LF(\n)(Unix/Linux下这个\n就就包括了回车和换行)。

2. 预备知识:缓冲区

#include<stdio.h>
#include<unistd.h>
  
int main()
{
   printf("hello world!");                                                                                                                                        
   sleep(3);
 
   return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

在这里插入图片描述

在linux下编译运行以上代码,发现在前三秒"hello world!"并未被打印到屏幕上,三秒之后被打印到屏幕上。那么此前三秒,它被储存到了那里呢?
答案是缓冲区,缓冲区(Buffer)主要应用于提高系统性能和效率。缓冲区通常指的是内存中的一段连续区域,用于临时存储数据,减少CPU、内存和外部设备(如硬盘、网络等)之间的交互次数。当应用程序向文件或设备进行读写操作时,数据会首先被存储到缓冲区中,然后再由缓冲区根据特定的刷新策略将数据写入磁盘或设备中。

缓冲区被刷新到显示器上的几种方式:

  • 程序结束的时候,一般要自动冲刷缓冲区
  • \n
  • 缓冲区满了,自动刷新
  • fflush() 函数强制刷新

3. 进度条

此文件夹下新建一个makefile文件:

processbar: Main.c Processbar.c
    gcc -o $@ $^         //选项 -o $@ 指定输出文件名,其中 $@ 是一个自动变量,代表当前规则的目标(在这里即为processbar)。而 $^ 是另一个自动变量,表示所有依赖文件的集合(即Main.c和Processbar.c)

.PHONY: clean
clean:
    rm -f processbar
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

这是一个用于将Main.cProcessbar.c源文件编译并链接成名为processbar的可执行文件的简单Makefile

这个Makefile提供了两种操作:
1.运行make processbar以编译并链接Main.cProcessbar.c源文件,生成processbar可执行文件。
2.运行make clean以删除processbar可执行文件,清理项目构建产物。

进度条代码版本一:(仅仅是进度条的模拟)

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

#define Length 101
#define Style '#'   //进度条填充样式为'#'字符
const char* lable="|/-\\";  //在进度条前方显示动画效果

void ProcBar();

////version 1
void ProcBar()
{
    char bar[Length];   //进度条的显示长度
    memset(bar,'\0',sizeof(bar));  //先初始化为'\0'填充清零
    int len=strlen(lable);

    int cnt=0;
    while(cnt<=100)    //使用循环结构,从0迭代到100,每次迭代表示进度的1%
    {
        printf("[%-100s][%-3d%%][%c]\r",bar,cnt,lable[cnt%len]);   //用回车并不是换行,每次打印进度条都从当前行头部开始打印
        fflush(stdout);    //强制刷新标准输出缓冲区,确保进度条立刻显示在屏幕上
        bar[cnt++]=Style;   //进度条数组bar的相应位置添加进度样式字符
        usleep(200000);    //用于休眠一段时间,模拟任务执行过程。(休眠单位为微秒)
    }

    printf("\n");
}

int main()
{
    ProcBar();

    return 0;
}
  • 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

这段C语言代码实现了一个简单的进度条功能,用于模拟某个长时间运行的任务(如文件下载、处理任务等)的进度展示。

效果演示:
在这里插入图片描述

进度条代码版本二:(模拟一个下载任务,根据下载任务的进度打印进度条)

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

#define Length 101
#define Style '#'   //进度条填充样式为'#'字符
const char* lable="|/-\\";  //在进度条前方显示动画效果

void ProcBar();

////version 2
////配合场景使用
//进度条每执行一次循环就刷新一次,会出现闪烁(我还挺喜欢这个的)
void ProcBar(double total, double current)    //total代表总任务量(即文件大小),current代表当前已完成的任务量(即已下载的数据量)
{
    char bar[Length];
    memset(bar, '\0', sizeof(bar));
    int len = strlen(lable);

    int cnt = 0;
    double rate = (current * 100.0) / total;
    int loop_count = (int)rate;       //计算进度百分比rate和对应的循环次数loop_count
    
    while (cnt <= loop_count)
    {
        printf("[%-100s][%.1lf%%][%c]\r", bar, rate, lable[cnt % len]);
        fflush(stdout);
        bar[cnt++] = Style;
        //usleep(200000);  //这个也不需要了,在另一个调它的函数download()中有
    }

    //printf("\n");
}

//模拟下载单个文件
void download()
{
    double filesize = 100 * 1024 * 1024 * 1.0; // 文件大小100兆
    double current = 0.0; // 当前下载量
    double bandwidth = 1024 * 1024 * 1.0; // 网络带宽1兆

    printf("download begin, current: %lf\n", current);
    while (current <= filesize)   //使用一个循环来模拟下载过程,每次迭代都更新current的值,并调用ProcBar函数来更新和显示进度条。
    {
        // 打印进度条
        ProcBar(filesize, current);

        // 从网络获取数据
        current += bandwidth;
        usleep(10000);    //模拟网络下载的速度
    }

    printf("\ndownload complete!   filesize:%lf\n", filesize);
}

int main()
{
    // 下载测试(模拟单个文件下载)
    // download();
    return 0;
}


  • 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
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63

这段代码实现了一个简单的进度条功能(ProcBar())和一个模拟单个文件下载的函数(download())。实际使用时,只需将download()中的相关参数替换为实际的下载信息,并调用此函数即可在控制台显示下载进度。

效果演示:
在这里插入图片描述

进度条代码版本三:(模拟多个下载任务,根据下载任务的进度打印进度条)

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

#define Length 101
#define Style '#'   //进度条填充样式为'#'字符
const char* lable="|/-\\";  //在进度条前方显示动画效果

typedef void(*callback_t)(double,double);  //函数指针  
void ProcBar();

//闪烁版
//void ProcBar(double total, double current)    //total代表总任务量(即文件大小),current代表当前已完成的任务量(即已下载的数据量)
//{
//    char bar[Length];
//    memset(bar, '\0', sizeof(bar));
//    int len = strlen(lable);
//
//    int cnt = 0;
//    double rate = (current * 100.0) / total;
//    int loop_count = (int)rate;       //计算进度百分比rate和对应的循环次数loop_count
//
//    while (cnt <= loop_count)
//    {
//        printf("[%-100s][%.1lf%%][%c]\r", bar, rate, lable[cnt % len]);
//        fflush(stdout);
//        bar[cnt++] = Style;
//        //usleep(200000);  //这个也不需要了,在另一个调它的函数download()中有
//    }
//
//    //printf("\n");
//}

// version 3
// 配合场景使用
// 每次调用函数时直接将bar拼接起来,循环执行完后再刷新,这样就不会出现闪烁
void ProcBar(double total, double current)
{
    char bar[Length];
    memset(bar, '\0', sizeof(bar));
    int len = strlen(lable);

    int cnt = 0;
    double rate = (current * 100.0) / total;
    int loop_count = (int)rate;       //计算进度百分比rate和对应的循环次数loop_count
    
    while (cnt <= loop_count)
    {
        bar[cnt++] = Style;
    }
    
    // 填充完成后,一次性打印出完整的进度条,并刷新输出,避免了闪烁现象
    printf("[%-100s][%.1lf%%][%c]\r", bar, rate, lable[cnt % len]);
    fflush(stdout);  
}
//此版本进度条与之前版本不同的是,它在循环中先将进度条字符逐个拼接到bar数组中,待循环完成后一次性刷新输出,从而避免了进度条的闪烁现象。

// 模拟下载多个文件
double bandwidth = 1024 * 1024 * 1.0;  // 网络带宽1兆
void download(double filesize, callback_t cb)   // 此处用函数指针也很方便
{
    double current = 0.0; // 当前下载量

    printf("download begin, current: %lf\n", current);
    while (current <= filesize)   //使用一个循环来模拟下载过程,每次迭代都更新当前下载量current,并调用回调函数cb来更新和显示进度条
    {
        cb(filesize, current);

        // 从网络获取数据
        current += bandwidth;
        usleep(10000);  //模拟下载速度时,每次迭代都增加bandwidth(网络带宽)的值到current,并使用usleep函数来模拟网络延迟
    }

    printf("\ndownload complete!   filesize:%lf\n", filesize);
}

int main()
{
    // 多个文件下载测试
    download(200 * 1024 * 1024, ProcBar);
    download(400 * 1024 * 1024, ProcBar);
    download(50 * 1024 * 1024, ProcBar);
    download(10 * 1024 * 1024, ProcBar);
    return 0;
}
  • 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
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85

这段C代码通过定义一个进度条更新函数ProcBar和一个模拟下载函数download,实现了在控制台中展示动态进度条的功能,并模拟了多个文件的下载过程。通过回调函数的使用,使得download函数可以灵活地与不同的进度条更新函数配合使用。

效果演示:
在这里插入图片描述

闪烁版演示效果:
在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/羊村懒王/article/detail/508428
推荐阅读
相关标签
  

闽ICP备14008679号