当前位置: 首页 > news >正文

【Linux系统】—— 简易进度条的实现

【Linux系统】—— 简易进度条的实现

  • 1 回车和换行
  • 2 缓冲区
  • 3 进度条的准备代码
  • 4 第一版进度条
  • 5 第二版进度条

1 回车和换行

  先问大家一个问题:回车换行是什么,或者说回车和换行是同一个概念吗?
  可能大家对回车换行有一定的误解,本文在这里讲解一下:

假设我们是在写小作文

  • 换行:将笔尖换到下一格,即移动到下一行。
  • 回车:将笔尖移动到本行的开头。
      
    在这里插入图片描述

  在计算机中,换行符为「\n」,回车符为 「\r」,我们往往用「\r\n」来整体表示回车换行。
  但之前写 C语言 时,我们只用「\n」也能同时达到回车和换行的效果,这是在语言层面上,将 \n 解析成 \r\n
  
  

2 缓冲区

  下面我们非常粗略的了解一下缓冲区的概念:

先来段测试代码

#include <stdio.h>
#include <unistd.h>int main()
{printf("Hello Linux!\n");sleep(3);return 0;
}

: sleep() 函数的头文件为:<unistd.h>
  

在这里插入图片描述

  一切正常
  我们将 printf 中的「\n」去掉试试

#include <stdio.h>
#include <unistd.h>int main()
{printf("Hello Linux!");sleep(3);return 0;
}

在这里插入图片描述

  结果好像和我们想的有点不太一样。

  解释上面原因前,先问大家一个问题:上述代码是先执行 printf还是先执行sleep
  按结果来看,应该是先执行 sleep,再执行printf
  但事实恰恰相反,是先执行的 printf。在初学C语言时,我们知道一个程序有几种控制流:循环、判断、顺序。我们对应的程序在执行时永远都是从前往后执行的
  
  当程序执行到 sleep 语句时,它一定是把 printf 执行完了。可显示器上并没有显示,那在休眠的 3 秒期间,字符串 “Hello Linux” 在哪呢?在缓冲区里!
  简单理解一下:在内存中有一块内存块叫缓冲区,先将字符串临时存放在缓冲区里。缓冲区向显示器输出是行刷新,也就是说它遇到 \n 自动将缓冲区的内容刷新出去;如果没遇到就一直在缓冲区中呆着,直到程序退出时再自动刷新缓冲区,我们才能看到打印出的字符串。

  如果想让不带 ‘/n’ 的字符串立即刷新,可以用 fflush 函数

#include <stdio.h>
#include <unistd.h>int main()
{printf("Hello Linux!");fflush(stdout);sleep(3);return 0;
}

在这里插入图片描述

  
  

3 进度条的准备代码

  我们先不急着写进度条,先写几段测试代码

  先来实现一个倒计时

int main()
{int i = 9;while(i >= 0){   printf("%d\n", i); --i;}   return 0;
}

在这里插入图片描述

  
  现在它是换行进行打印,但我们想让它在同一个位置打印。这时我们可以运用前面学习到的回车符 「\r」

int main()
{int i = 9;while(i >= 0){   printf("%d\r", i); --i;sleep(1);}   return 0;
}

在这里插入图片描述

  为什么它一直不显示呢?而且最后程序结束了,命令行覆盖了,什么都没有
  这是因为数据一直在缓冲区没有刷新出来,而最后程序运行结束刷新缓冲区了,因为回车符,命令行从头开始写将数据覆盖了。

  我们手动刷新缓冲区,并且为了不让命令行覆盖数据,我们单独进行换行

int main()
{int i = 9;while(i >= 0){   printf("%d\r", i); fflush(stdout);--i;sleep(1);}   printf("\n");return 0;
}

在这里插入图片描述

  现在,我们写的代码就可以进行倒计时了……了吗?

  还没有,如果我们改成从 10 开始倒计时会怎样

在这里插入图片描述

  又出问题了。

  讲个小知识点:显示
  当我们向显示器中输出 12345 这个数时,显示器上本质上是输出 12345 这个数字还是 ‘1’ ‘2’ ‘3’ ‘4’ ‘5’ 这 5 个字符呢?
  答案是后者显示器是字符设备,它只认字符
  这也解释了为什么我们 printf 要格式化输出。比如我们输出一个 int a,printf 内部将其由整数转成字符串,再用类似 putc 的接口一个一个字符输出到显示器上

  怎么解决上述问题呢?很简单,将输出的显示的位宽改为 2 即可

int main()
{int i = 10; while(i >= 0){   printf("%-2d\r", i); fflush(stdout);--i;sleep(1);}   printf("\n");return 0;
}

  

4 第一版进度条

  我们想写一个怎么样的进度条:
  一对 [ ] 括起100个空字符;每加载 1% 就有一个 ‘#’ 替换一空字符;并在后面显示下载进度和转圈圈表示软件一直在下载

在这里插入图片描述

  
  首先创建多文件

在这里插入图片描述

  

  再写 makefile(对 makefile 有困惑的小伙伴可移步【Linux系统】—— make/makefile)

在这里插入图片描述

  
  基本框架如下:

在这里插入图片描述

  
  process.c 代码如下

在这里插入图片描述

  如果大家觉得休眠 1 秒时间太长,这里给大家再介绍一个新的休眠函数:usleep
  usleep 函数的休眠时间是以微妙为单位的,头文件同样是<unistd.h>

  效果如下:

在这里插入图片描述

  现在我们还需要加上百分比旋转光标。旋转光标是用来告诉用户该程序一直在下载中。
  百分比的实现很简单,这里就不单独介绍了,需要注意的是打印 ‘%’,要输入 ‘%%’ 表示取其字面值
  
  我们简单介绍一下简易光标如何实现
  其实旋转光标很简单,只需要 '|' '/' '-' '\' 不断循环打印即可(因为 '\' 是特殊字符,我们输入 '\\' 表示取字面值)
  
  至此,我们就完成了第一版进度条,代码如下

#include "process.h"
#include <string.h>
#include <unistd.h>#define NUM 101
#define STYLE '#'void process_v1()
{char buffer[NUM];memset(buffer, 0, sizeof(buffer));const char* lable = "|/-\\";int len = strlen(lable);int cnt = 0;while(cnt <= 100){   printf("[%-100s][%2d%%][%c]\r", buffer, cnt, lable[cnt % len]);fflush(stdout);buffer[cnt] = STYLE;++cnt;usleep(100000);}   printf("\n");
}

效果展示:

在这里插入图片描述

  
  

5 第二版进度条

  第一版本的进度条看起来像模像样的,其实它根本无法使用

  因为第一版本的进度条和下载的软件是各跑各的,可能软件值下载了 1% 但我们的进度条已经跑完了,而真实的进度条是要反应真实下载进度的。

  一个进度条,一定要结合具体的场景,边下载边更新进度条。

  我们定义变量 total 来表示要下载的总大小;speed 为下载的速度,当然实际的下载速度是浮动的,但这里为方便我们将其固定下来;current 表示当前已下载量。当然,真正的下载是要从网络中获取数据的,这点我们还没学,就用休眠时间来替代

// main.c
#include "process.h"
#include <unistd.h>
#include <stdio.h>double total = 1024;
double speed = 1.0;void DownLoad()
{double current = 0;while(current < total){   //下载代码usleep(3000);//充当下载数据current += speed;}   printf("download %lfMB Done\n", current);
}int main()
{DownLoad();return 0;
}

  现在的情况是我们不知道他正在下载,因此我们需要引入进度条。
  我们重新定义一个 FlushProcess()函数,FlushProcess()函数 的作用是根据当前的下载进度来打印进度条

部分代码如下

void FlushProcess(double total, double current)
{char buffer[NUM];memset(buffer, 0, sizeof(buffer));const char* lable = "|/-\\";int len = strlen(lable);static int cnt = 0;//不需要自己循环,填充#int num = (int)(current * 100 / total);int i = 0;for(; i < num; i++){   buffer[i] = STYLE;}   double rate = current / total;cnt %= len;printf("[%-100s][%.lf%%][%c]\r", buffer, rate * 100, lable[cnt]);++cnt;fflush(stdout);
}void DownLoad()
{double current = 0;while(current <= total){   //调用FlushProcess函数不断打印进度条FlushProcess(total, current);//下载代码usleep(3000);//充当下载数据current += speed;}   printf("\ndownload %.2lfMB Done\n", current);
}int main()
{DownLoad();return 0;
}

效果演示

在这里插入图片描述

  
  但是上述代码还有一点小问题:现在是下载需要进度条,如果以后是上传呢?上传也需要有对应的进度条。但此时的进度条函数 FlushProcess() 是硬加载在下载函数 DownLoad() 中的;如果是上传,需要的函数是 UpLoad(),那是不是在 UpLoad()函数中也要加载一份进度条 FlushProcess() 函数呢?这样做是不是太麻烦了?

  为了解决这个问题,我们可以使用函数指针

完整代码:

//process.h
#pragma one
#include <stdio.h>void FlushProcess(double total, double current);//process.c
#include "process.h"
#include <string.h>
#include <unistd.h>#define NUM 101
#define STYLE '#'void FlushProcess(double total, double current)
{char buffer[NUM];memset(buffer, 0, sizeof(buffer));const char* lable = "|/-\\";int len = strlen(lable);static int cnt = 0;//不需要自己循环,填充#int num = (int)(current * 100 / total);int i = 0;for(; i < num; i++){   buffer[i] = STYLE;}   double rate = current / total;cnt %= len;printf("[%-100s][%.lf%%][%c]\r", buffer, rate * 100, lable[cnt]);++cnt;fflush(stdout);
}//main.c
include "process.h"
#include <unistd.h>
#include <stdio.h>typedef void(*callback_t)(double total, double current);double total = 1024;
double speed = 1.0;void DownLoad(callback_t cb) 
{double current = 0;while(current <= total){   cb(total, current);//下载代码usleep(3000);//充当下载数据current += speed;}   printf("\ndownload %.2lfMB Done\n", current);
}int main()
{DownLoad(FlushProcess);return 0;
}

  
  
  
  


  好啦,本期关于 进度条 的知识就介绍到这里啦,希望本期博客能对你有所帮助。同时,如果有错误的地方请多多指正,让我们在 Linux 的学习路上一起进步!

相关文章:

【Linux系统】—— 简易进度条的实现

【Linux系统】—— 简易进度条的实现 1 回车和换行2 缓冲区3 进度条的准备代码4 第一版进度条5 第二版进度条 1 回车和换行 先问大家一个问题&#xff1a;回车换行是什么&#xff0c;或者说回车和换行是同一个概念吗&#xff1f;   可能大家对回车换行有一定的误解&#xff0…...

Qt 中使用 SQLite 数据库的完整指南

SQLite 是一款轻量级、嵌入式的关系型数据库&#xff0c;无需独立的服务器进程&#xff0c;数据以文件形式存储&#xff0c;非常适合桌面和移动端应用的本地数据管理。Qt 通过 Qt SQL 模块提供了对 SQLite 的原生支持&#xff0c;开发者可以轻松实现数据库的增删改查、事务处理…...

数智化时代的工单管理:从流程驱动到数据驱动-亿发

在数智化时代&#xff0c;工单管理系统已从简单的任务分发工具演变为企业运营的智能中枢。传统工单系统关注流程的线性推进&#xff0c;而现代工单管理系统则强调数据的全生命周期管理&#xff0c;通过智能算法实现工单的自动分配、优先级判定和效能优化。这种转变不仅提升了运…...

Large Language Model Distilling Medication Recommendation Model

摘要&#xff1a;药物推荐是智能医疗系统的一个重要方面&#xff0c;因为它涉及根据患者的特定健康需求开具最合适的药物。不幸的是&#xff0c;目前使用的许多复杂模型往往忽视医疗数据的细微语义&#xff0c;而仅仅严重依赖于标识信息。此外&#xff0c;这些模型在处理首次就…...

floodfill算法系列一>被围绕的区域

目录 整体思想&#xff1a;代码设计&#xff1a;代码呈现&#xff1a; 整体思想&#xff1a; 代码设计&#xff1a; 代码呈现&#xff1a; class Solution {int m,n;int[] dx {0,0,-1,1};int[] dy {-1,1,0,0};public void solve(char[][] board) {m board.length;n board[…...

Redis 01 02章——入门概述与安装配置

一、入门概述 &#xff08;1&#xff09;是什么 Redis&#xff1a;REmote Dictionary Server&#xff08;远程字典服务器&#xff09;官网解释&#xff1a;Remote Dictionary Server(远程字典服务)是完全开源的&#xff0c;使用ANSIC语言编写遵守BSD协议&#xff0c;是一个高…...

windows基于cpu安装pytorch运行faster-whisper-large-v3实现语音转文字

1.创建虚拟环境 conda create -n faster-whisper python3.10 conda activate faster-whisper 2.安装cpu版本的pytorch pip3 install torch torchvision torchaudio -i https://pypi.tuna.tsinghua.edu.cn/simple 3.验证pytorch安装结果 (faster-whisper) H:\big-model\faste…...

AI大模型(如GPT、BERT等)可以通过自然语言处理(NLP)和机器学习技术,显著提升测试效率

在软件测试中,AI大模型(如GPT、BERT等)可以通过自然语言处理(NLP)和机器学习技术,显著提升测试效率。以下是几个具体的应用场景及对应的代码实现示例: 1. 自动生成测试用例 AI大模型可以根据需求文档或用户故事自动生成测试用例。 代码示例(使用 OpenAI GPT API): …...

【Prometheus】prometheus黑盒监控balckbox全面解析与应用实战

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全…...

CSS实现单行、多行文本溢出显示省略号(…)

在网页设计中&#xff0c;我们常常遇到这样的情况&#xff1a;文本内容太长&#xff0c;无法完全显示在一个固定的区域内。为了让界面看起来更整洁&#xff0c;我们可以使用省略号&#xff08;…&#xff09;来表示内容溢出。这不仅能提升用户体验&#xff0c;还能避免内容溢出…...

服务器中部署大模型DeepSeek-R1 | 本地部署DeepSeek-R1大模型 | deepseek-r1部署详细教程

0. 部署前的准备 首先我们需要足够算力的机器&#xff0c;这里我在vultr中租了有一张A16显卡一共16GB显存的服务器作为演示。部署的模型参数为14b的。如果需要部署满血版本671b的&#xff0c;需要更大的算力支持&#xff0c;这里由于是个人资金有限&#xff0c;就演示14b的部署…...

元学习之孪生网络Siamese Network

简介&#xff1a;元学习是一种思想&#xff0c;一般以神经网络作为特征嵌入的工具&#xff0c;实现对数据特征的提取&#xff0c;然后通过构造某种指标以引导优化器对模型参数进行优化。而最小化距离是最常见的学习目标&#xff0c;这就是熟知的度量学习&#xff0c;度量学习里…...

深入HBase——引入

引入 前面我们通过深入HDFS到深入MapReduce &#xff0c;从设计和落地&#xff0c;去深入了解了大数据最底层的基石——存储与计算是如何实现的。 这个专栏则开始来看大数据的三驾马车中最后一个。 通过前面我们对于GFS和MapReduce论文实现的了解&#xff0c;我们知道GFS在数…...

Python创建FastApi项目模板

1. 项目结构规范 myproject/ ├── app/ │ ├── core/ # 核心配置 │ │ ├── config.py # 环境配置 │ │ └── security.py # 安全配置 │ ├── routers/ # 路由模块 │ │ └── users.py # 用户路由 │ ├…...

TCNE 网络安全

一.概况 CTF&#xff08;Capture The Flag&#xff09;在网络安全领域中指的是网络技术人员之间进行技术竞技的一种比赛形式&#xff0c;它起源于1996年的DEFCON全球黑客大会&#xff0c;以代替之前黑客们通过互相发起真实攻击进行技术比拼的方式&#xff0c;现已成为全球范围网…...

车规MCU处理器选择Cortex-M7还是Cortex-R52?

车规mcu处理器选择Cortex-M7还是Cortex-R52&#xff1f;跟随小编从具体应用场景、安全等级&#xff08;ASIL&#xff09;、性能、成本进行分析吧。 01安全等级需求 ASIL-D&#xff08;如刹车、转向&#xff09;→ 必选R52。R52原生支持ASIL-D&#xff0c;硬件级错误检测&#…...

什么是计算机中的 “终端”?

在我们初学编程的时候&#xff0c;会遇到一个很重要的概念 ——终端。那它到底是什么呢&#xff1f; 在计算机领域&#xff0c;终端就像是我们和计算机进行对话的 “窗口”。我们可以在这个窗口里&#xff0c;用一些特定的命令来告诉计算机该做什么。比如&#xff0c;让计算机…...

LeetCode刷题---字符串---819

最常见的单词 819. 最常见的单词 - 力扣&#xff08;LeetCode&#xff09; 题目&#xff1a; 给你一个字符串 paragraph 和一个表示禁用词的字符串数组 banned &#xff0c;返回出现频率最高的非禁用词。题目数据 保证 至少存在一个非禁用词&#xff0c;且答案 唯一 。 par…...

SSH IBM AIX服务器相关指标解读

&#xff08;一&#xff09;ZPU使用率 含义 在IBM AIX服务器中&#xff0c;ZPU使用率反映了特定处理单元&#xff08;ZPU&#xff0c;假设是某种自定义或特定环境下的处理单元&#xff09;的资源利用程度。它表示ZPU在一段时间内处于忙碌状态执行任务的时间比例。例如&#xff…...

Wireshark TS | 再谈虚假的 TCP Spurious Retransmission

前言 在之前的《虚假的 TCP Spurious Retransmission》文章中曾提到一个错误判断为 TCP Spurious Retransmission&#xff0c;实际为 TCP Out-Of-Order 的案例&#xff0c;本次继续探讨一个虚假的 TCP Spurious Retransmission 案例。 问题背景 TCP Spurious Retransmission…...

springboot 百货中心供应链管理系统小程序

一、前言 随着我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受用户的喜爱&#xff0c;百货中心供应链管理系统被用户普遍使用&#xff0c;为方…...

高等数学(下)题型笔记(八)空间解析几何与向量代数

目录 0 前言 1 向量的点乘 1.1 基本公式 1.2 例题 2 向量的叉乘 2.1 基础知识 2.2 例题 3 空间平面方程 3.1 基础知识 3.2 例题 4 空间直线方程 4.1 基础知识 4.2 例题 5 旋转曲面及其方程 5.1 基础知识 5.2 例题 6 空间曲面的法线与切平面 6.1 基础知识 6.2…...

C# 求圆面积的程序(Program to find area of a circle)

给定半径r&#xff0c;求圆的面积。圆的面积应精确到小数点后5位。 例子&#xff1a; 输入&#xff1a;r 5 输出&#xff1a;78.53982 解释&#xff1a;由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982&#xff0c;因为我们只保留小数点后 5 位数字。 输…...

windows系统MySQL安装文档

概览&#xff1a;本文讨论了MySQL的安装、使用过程中涉及的解压、配置、初始化、注册服务、启动、修改密码、登录、退出以及卸载等相关内容&#xff0c;为学习者提供全面的操作指导。关键要点包括&#xff1a; 解压 &#xff1a;下载完成后解压压缩包&#xff0c;得到MySQL 8.…...

Vue 模板语句的数据来源

&#x1f9e9; Vue 模板语句的数据来源&#xff1a;全方位解析 Vue 模板&#xff08;<template> 部分&#xff09;中的表达式、指令绑定&#xff08;如 v-bind, v-on&#xff09;和插值&#xff08;{{ }}&#xff09;都在一个特定的作用域内求值。这个作用域由当前 组件…...

React从基础入门到高级实战:React 实战项目 - 项目五:微前端与模块化架构

React 实战项目&#xff1a;微前端与模块化架构 欢迎来到 React 开发教程专栏 的第 30 篇&#xff01;在前 29 篇文章中&#xff0c;我们从 React 的基础概念逐步深入到高级技巧&#xff0c;涵盖了组件设计、状态管理、路由配置、性能优化和企业级应用等核心内容。这一次&…...

JS红宝书笔记 - 3.3 变量

要定义变量&#xff0c;可以使用var操作符&#xff0c;后跟变量名 ES实现变量初始化&#xff0c;因此可以同时定义变量并设置它的值 使用var操作符定义的变量会成为包含它的函数的局部变量。 在函数内定义变量时省略var操作符&#xff0c;可以创建一个全局变量 如果需要定义…...

python打卡day49@浙大疏锦行

知识点回顾&#xff1a; 通道注意力模块复习空间注意力模块CBAM的定义 作业&#xff1a;尝试对今天的模型检查参数数目&#xff0c;并用tensorboard查看训练过程 一、通道注意力模块复习 & CBAM实现 import torch import torch.nn as nnclass CBAM(nn.Module):def __init__…...

计算机系统结构复习-名词解释2

1.定向&#xff1a;在某条指令产生计算结果之前&#xff0c;其他指令并不真正立即需要该计算结果&#xff0c;如果能够将该计算结果从其产生的地方直接送到其他指令中需要它的地方&#xff0c;那么就可以避免停顿。 2.多级存储层次&#xff1a;由若干个采用不同实现技术的存储…...

智警杯备赛--excel模块

数据透视与图表制作 创建步骤 创建 1.在Excel的插入或者数据标签页下找到数据透视表的按钮 2.将数据放进“请选择单元格区域“中&#xff0c;点击确定 这是最终结果&#xff0c;但是由于环境启不了&#xff0c;这里用的是自己的excel&#xff0c;真实的环境中的excel根据实训…...