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;
}

运行效果:

相关推荐
袁袁袁袁满1 小时前
Linux防火墙UFW和宝塔显示不同步问题如何解决?
linux·运维·服务器·宝塔·防火墙ufw
悲伤小伞1 小时前
Linux_传输层协议Udp
linux·网络协议·udp
程序员敲代码吗1 小时前
虚拟机内部工作机制揭秘:深入解析栈帧
java·linux·jvm
REDcker1 小时前
FFmpeg完整文档
linux·服务器·c++·ffmpeg·音视频·c·后端开发
之歆2 小时前
Linux 集群与负载均衡(LVS)
linux·负载均衡·lvs
地球空间-技术小鱼2 小时前
搜罗Linux桌面环境(Desktop Environments)列表
linux·运维·服务器·笔记·学习·ubuntu·debian
gfdgd xi2 小时前
GXDE OS 25.3.1 更新了!修复更多 bug 了!
linux·c++·操作系统·bug·deepin
Trouvaille ~2 小时前
【Linux】TCP vs UDP深度对比:如何选择与用UDP实现可靠传输
linux·网络·c++·tcp/ip·udp·操作系统
岳清源2 小时前
LVS章节
linux