006-Linux第一个小程序-进度条-初步理解缓冲区

Linux第一个小程序-进度条-初步理解缓冲区

1. 换行和回车

我们在C语言中学习的字符\n实际上是由两个部分组成的,分别是换行和回车。

在以前的打字机上,打字的时候,打满一行以后,打字机需要换行进行打印,换行以后打印的位置还是在那一行的最结尾,但是我们应当要从行首进行打印,此时就需要先回到这一行的行首,这个动作就叫做回车。

在C语言中的\r的作用就是回车(回到这一行的行首),而\n在C语言中的作用是回车+换行。

2. 缓冲区

接下来看这两段代码:

在上面代码中,sleep(3)函数的意思是,代码运行到这里时,休眠三秒,然后再往下运行,使用这个函数需要包含头文件unistd.h,具体介绍可以通过man 3 sleep进行查询。

这两段代码,逻辑上来说,结果应该是一摸一样的,实际上最后的结果也确实是一样的,但是不一样的是,第一张图的代码编译运行后,123456789马上就会被打印出来,而第二张图的代码运行后,123456789将会隔3秒后再运行。

这是为什么呢?原因在于其实我们使用printf时,它并不是马上就打印出来的,在使用printf时,它会先将我们需要打印的内容存在一个缓存区里面,当下面几种情况下,会立即将缓存区内的内容刷新到屏幕上:

  1. 当缓存区内需要进行换行时:也就是当有一个\n进入了缓存区时,缓存区中的这一行内容将会立即被刷新到屏幕上。

  2. 当缓存区存满时:也就是printf的缓存区已经被需要打印到屏幕上的信息存满了,此时缓存区的内容会被立即刷新到屏幕上,然后接收后面的内容。

  3. 当程序即将结束时:当程序马上要退出的时候,此时缓存区确定不会再有新的内容进来了,就会将缓存区中的内容立即刷新到屏幕上。

  4. 当使用fflush函数强制刷新时:我们可以使用fflush(stdout)强制刷新缓存区,使里面的内容立即被打印到屏幕上。

关于流,在C语言程序运行的时候,默认打开了三个流,分别是:标准输入流stdin、标准输出流stdout、标准错误流stderr,其中,stdin从键盘输入的流,stdout和stderr都是默认输出到屏幕的流,其实我们使用到printf就是向默认打开的stdout输出,关于这些内容在后面会详细介绍。

fflush(stdout)就是将stdout的缓存区内容刷新到stdout中。

3. 基于前面的内容写一个进度条

基于上面学习的回车和缓存区的知识,我们就可以写一段代码,实现一个进度条小程序。

复制代码
// Processbar.h
#pragma once

// 将void(double, double)函数类型重命名为callback_t(回调函数)
typedef void(*callback_t)(double, double);

// 进度条函数的声明
void ProcBar(double total, double current);

// Processbar.c
#include "Processbar.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define Length 52 // 进度条的长度
#define Fill '-' // 用于初始化填充进度条的字符
#define Style '=' // 用于填充进度中进度的字符

const char *lable = "|/-\\"; // 用于显示加载时转圈的效果

// 进度条函数的定义,打印单次进度条,打印完后使光标回到行首(\r),根据输入的total和current计算出当前的完成的进度比例,根据完成的进度打印出进度条。
void ProcBar(double total, double current)
{
  char bar[Length]; // 进度条字符串
  memset(bar, Fill, sizeof(bar)); // 初始化进度条字符串
  int len = strlen(lable); // 计算加载圈字符串的长度
  double rate = (current * 100) / total; // 计算当前进度比例
  int cnt = 0;
  while (cnt * 2 <= rate)
    bar[++cnt] = Style; // 使用Style填充进度条完成的部分
  printf("[%c][%-20s][%5.1lf%%]\r", lable[cnt % len], bar + 1, rate); // 打印进度条
  fflush(stdout); // 刷新缓存区的内容到屏幕
}
c 复制代码
// main.c
#include "Processbar.h"
#include <stdio.h>
#include <unistd.h>

// 设置时间间隔(单位时间)
int interval = 100000;
// 设带宽(每单位时间能获取到的数据量)
double bandwidth = 1000000;

// 下载函数,filesize为需要下载的文件大小,cb为回调函数,也就是上面写的打印进度条的函数。
void download(double filesize, callback_t cb)
{
  double current = 0.0;
  printf("download begin, current: %lf\n", current);
  // 每轮获取数据后打印一次进度条
  while (current <= filesize)
  {
  	// 单次打印进度条
    cb(filesize, current);
    // 模拟从网络中获取数据,每interval从网络中获取bandwidth大小的数据
    usleep(interval);
    // 文件传输完成后跳出循环,完成下载。
    if (current == filesize) break;
    current += (filesize - current > bandwidth ? bandwidth : filesize - current); // 如果当前还差的数据量>带宽,则增加带宽大小的数据量,使当前数据量等于文件大小(即传输完成)。
  }
  printf("\ndownload begin, filesize: %lf\n", filesize);
}

int main()
{
  download(100 * 1024 * 1024, ProcBar);
  download(73829483, ProcBar);

  return 0;
}

运行效果:

相关推荐
Sheffield20 小时前
Alpine是什么,为什么是Docker首选?
linux·docker·容器
Johny_Zhao2 天前
centos7安装部署openclaw
linux·人工智能·信息安全·云计算·yum源·系统运维·openclaw
haibindev2 天前
在 Windows+WSL2 上部署 OpenClaw AI员工的实践与踩坑
linux·wsl2·openclaw
0xDevNull3 天前
Linux切换JDK版本详细教程
linux
进击的丸子3 天前
虹软人脸服务器版SDK(Linux/ARM Pro)多线程调用及性能优化
linux·数据库·后端
Johny_Zhao4 天前
OpenClaw安装部署教程
linux·人工智能·ai·云计算·系统运维·openclaw
chlk1236 天前
Linux文件权限完全图解:读懂 ls -l 和 chmod 755 背后的秘密
linux·操作系统
舒一笑6 天前
Ubuntu系统安装CodeX出现问题
linux·后端
改一下配置文件6 天前
Ubuntu24.04安装NVIDIA驱动完整指南(含Secure Boot解决方案)
linux