【Linux庖丁解牛】—Linux第一个系统程序—进度条!

发布于:2025-02-10 ⋅ 阅读:(36) ⋅ 点赞:(0)

目录

前言:

1、回车与换行

历史背景

不同操作系统中的使用

标准输入输出函数

2、行缓冲区

3、进度条version1

4、进度条version2(模拟下载环境)


前言:

在实现进度条之前,这里我们要先铺垫两个概念——回车换行与行缓冲区。

1、回车与换行

在计算机科学和文本处理中,“回车”(Carriage Return,简称 CR,ASCII 码为 13)和“换行”(Line Feed,简称 LF,ASCII 码为 10)是两个不同的概念,但在实际使用中,它们经常联系在一起,用于表示文本行的结束和新行的开始。

历史背景

  • 回车(Carriage Return, CR, \r):在早期的打字机或电传打字机中,回车操作会使打印头移动到当前行的最左端(即回到行首)。
  • 换行(Line Feed, LF, \n):换行操作会使打印头移动到下一行的同一位置(即向下移动一行)。

不同操作系统中的使用

  • Unix/Linux/macOS:这些系统使用 LF(\n)作为行结束符。
  • Windows:Windows 系统最初使用 CR+LF(\r\n)作为行结束符,这是从早期的 DOS 系统继承下来的。这种组合最初是为了兼容当时的打印机和终端。
  • 经典 Mac OS(9 及更早版本):这些系统使用 CR(\r)作为行结束符。

在C语言中,处理文本行结束符时,同样需要考虑到不同操作系统中回车(Carriage Return, CR, \r)和换行(Line Feed, LF, \n)的差异。然而,C语言标准库提供了一些函数和宏,可以帮助程序员处理这些差异,而无需过多关注底层细节。

标准输入输出函数

C语言标准库中的printfscanffgetsfputs等函数通常会自动处理行结束符,以适应不同的操作系统。例如,当使用printf输出一个字符串并包含一个换行符\n时,在Windows系统上,标准输出库函数会将其转换为\r\n(如果输出到控制台或文件),而在Unix/Linux/macOS系统上,则只会输出\n

2、行缓冲区

我们先来看三个个现象:

现象一:

先打印并换行。 

 现象二:

 现象三:

先打印不回车换行。

第一个问题:为什么我们在基于现象一后将源文件中的\n去掉后会出现现象二呢?

难道是程序先执行sleep函数,再执行printf函数吗?答案是否定的,在我们学习C语言时就知道,程序从main函数开始从上至下执行代码。所以,这里一定是先执行printf函数,再执行sleep函数。

那原因到底是什么呢?

这里我们就要引入行缓冲区的概念了:

在C语言中,行缓冲区(line-buffered)是三种类型的输入/输出缓冲区之一,另外还有全缓冲区(fully-buffered)和无缓冲区(unbuffered)。行缓冲区的行为取决于所使用的标准I/O函数和流(stream)。

行缓冲区的特点:当遇到换行符(\n)或者缓冲区满时,缓冲区内容会被刷新(即写入到目标设备)。

解释:当printf函数执行完后,其内容并没有打印到显示器上,而是在缓冲区里面(没有遇到换行符(\n)或者缓冲区满)。然后执行完sleep函数后,程序结束会自动刷新缓冲区,最后才打印出内容“Hello Linux!”。

第二个问题:现象三为什么会先打印后休眠?

这里我们就介绍一下fflush函数:

  • fflush(FILE *stream):刷新指定流的缓冲区。即使缓冲区没有满或没有遇到换行符,调用这个函数也会强制将缓冲区内容写入目标设备。

3、进度条version1

#include"process.h"

#define NUM 101
#define STYLE '='

void processbar()
{
//buffer数组模拟进度条
    char buffer[NUM];
    memset(buffer, 0, sizeof(buffer));
    //lable字符串模拟旋转效果
    const char* lable = "\\-|/";
    int size = strlen(lable);
    int cnt = 0;
    while (cnt <= 100)
    {
        printf("[%-100s][%d%%][%c]\r", buffer, cnt, lable[cnt % size]);
        fflush(stdout);
        buffer[cnt] = STYLE;
        usleep(50000);
        cnt++;
     }
    printf("\n");
}

效果演示: 

4、进度条version2(模拟下载环境)

第一个版本我们只是将进度条的运行效果演示成功了,在实际程序下载的过程中我们不可能直接将上面的程序当做进度条,不然很大可能,我们的程序还没有下载完,而进度条就已经跑完了,这是不合理的!

所以,在实际场景中,我们要根据下载的总量和当前的下载量,来决定进度条的进度!

具体如何操作,大家看下面的代码就完全可以看懂啦!(非常简单的)

process.c

#include"process.h"

void process(double total, double cur)
{
    //改函数会被下载的任务频繁调用,所以cnt设为静态
    static int cnt = 0;
    char buffer[NUM];
    memset(buffer, 0, sizeof(buffer));
    
    //模拟旋转效果
    const char* lable = "\\-|/";
    int len = strlen(lable);
   
    //找到当前进度的位置
    int num = (int)(cur * 100) / total;
    int i = 0;
    for (; i < num; i++)
    {
        buffer[i] = STYLE;
    }
    //不再循环打印,直接根据当前下载进度打印出进度条
    printf("[%-100s][%.1f%%][%c]\r",
            buffer, cur / total * 100, lable[cnt % len]);
    fflush(stdout);
    cnt++;
}

main.c

#include"process.h"

double total = 1024.0;
double speed = 1.0;//加载速度
//该函数用于模拟程序的下载
void DownLoad()
{
    double cur = 0;
    while (cur <= total)
    {
        process(total, cur);
        usleep(3000);
        cur += speed;
    }
    printf("\n");
}



int main()
{
    DownLoad();
    DownLoad();
    DownLoad();
    DownLoad();
    return 0;
}

效果演示: 


网站公告

今日签到

点亮在社区的每一天
去签到