当前位置: 首页 > 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…...

手游刚开服就被攻击怎么办?如何防御DDoS?

开服初期是手游最脆弱的阶段&#xff0c;极易成为DDoS攻击的目标。一旦遭遇攻击&#xff0c;可能导致服务器瘫痪、玩家流失&#xff0c;甚至造成巨大经济损失。本文为开发者提供一套简洁有效的应急与防御方案&#xff0c;帮助快速应对并构建长期防护体系。 一、遭遇攻击的紧急应…...

stm32G473的flash模式是单bank还是双bank?

今天突然有人stm32G473的flash模式是单bank还是双bank&#xff1f;由于时间太久&#xff0c;我真忘记了。搜搜发现&#xff0c;还真有人和我一样。见下面的链接&#xff1a;https://shequ.stmicroelectronics.cn/forum.php?modviewthread&tid644563 根据STM32G4系列参考手…...

遍历 Map 类型集合的方法汇总

1 方法一 先用方法 keySet() 获取集合中的所有键。再通过 gey(key) 方法用对应键获取值 import java.util.HashMap; import java.util.Set;public class Test {public static void main(String[] args) {HashMap hashMap new HashMap();hashMap.put("语文",99);has…...

UDP(Echoserver)

网络命令 Ping 命令 检测网络是否连通 使用方法: ping -c 次数 网址ping -c 3 www.baidu.comnetstat 命令 netstat 是一个用来查看网络状态的重要工具. 语法&#xff1a;netstat [选项] 功能&#xff1a;查看网络状态 常用选项&#xff1a; n 拒绝显示别名&#…...

蓝桥杯 2024 15届国赛 A组 儿童节快乐

P10576 [蓝桥杯 2024 国 A] 儿童节快乐 题目描述 五彩斑斓的气球在蓝天下悠然飘荡&#xff0c;轻快的音乐在耳边持续回荡&#xff0c;小朋友们手牵着手一同畅快欢笑。在这样一片安乐祥和的氛围下&#xff0c;六一来了。 今天是六一儿童节&#xff0c;小蓝老师为了让大家在节…...

如何在看板中有效管理突发紧急任务

在看板中有效管理突发紧急任务需要&#xff1a;设立专门的紧急任务通道、重新调整任务优先级、保持适度的WIP&#xff08;Work-in-Progress&#xff09;弹性、优化任务处理流程、提高团队应对突发情况的敏捷性。其中&#xff0c;设立专门的紧急任务通道尤为重要&#xff0c;这能…...

Nuxt.js 中的路由配置详解

Nuxt.js 通过其内置的路由系统简化了应用的路由配置&#xff0c;使得开发者可以轻松地管理页面导航和 URL 结构。路由配置主要涉及页面组件的组织、动态路由的设置以及路由元信息的配置。 自动路由生成 Nuxt.js 会根据 pages 目录下的文件结构自动生成路由配置。每个文件都会对…...

高危文件识别的常用算法:原理、应用与企业场景

高危文件识别的常用算法&#xff1a;原理、应用与企业场景 高危文件识别旨在检测可能导致安全威胁的文件&#xff0c;如包含恶意代码、敏感数据或欺诈内容的文档&#xff0c;在企业协同办公环境中&#xff08;如Teams、Google Workspace&#xff09;尤为重要。结合大模型技术&…...

C++中string流知识详解和示例

一、概览与类体系 C 提供三种基于内存字符串的流&#xff0c;定义在 <sstream> 中&#xff1a; std::istringstream&#xff1a;输入流&#xff0c;从已有字符串中读取并解析。std::ostringstream&#xff1a;输出流&#xff0c;向内部缓冲区写入内容&#xff0c;最终取…...

音视频——I2S 协议详解

I2S 协议详解 I2S (Inter-IC Sound) 协议是一种串行总线协议&#xff0c;专门用于在数字音频设备之间传输数字音频数据。它由飞利浦&#xff08;Philips&#xff09;公司开发&#xff0c;以其简单、高效和广泛的兼容性而闻名。 1. 信号线 I2S 协议通常使用三根或四根信号线&a…...