【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 回车和换行 先问大家一个问题:回车换行是什么,或者说回车和换行是同一个概念吗? 可能大家对回车换行有一定的误解࿰…...
Qt 中使用 SQLite 数据库的完整指南
SQLite 是一款轻量级、嵌入式的关系型数据库,无需独立的服务器进程,数据以文件形式存储,非常适合桌面和移动端应用的本地数据管理。Qt 通过 Qt SQL 模块提供了对 SQLite 的原生支持,开发者可以轻松实现数据库的增删改查、事务处理…...

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

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

floodfill算法系列一>被围绕的区域
目录 整体思想:代码设计:代码呈现: 整体思想: 代码设计: 代码呈现: 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章——入门概述与安装配置
一、入门概述 (1)是什么 Redis:REmote Dictionary Server(远程字典服务器)官网解释:Remote Dictionary Server(远程字典服务)是完全开源的,使用ANSIC语言编写遵守BSD协议,是一个高…...
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实现单行、多行文本溢出显示省略号(…)
在网页设计中,我们常常遇到这样的情况:文本内容太长,无法完全显示在一个固定的区域内。为了让界面看起来更整洁,我们可以使用省略号(…)来表示内容溢出。这不仅能提升用户体验,还能避免内容溢出…...

服务器中部署大模型DeepSeek-R1 | 本地部署DeepSeek-R1大模型 | deepseek-r1部署详细教程
0. 部署前的准备 首先我们需要足够算力的机器,这里我在vultr中租了有一张A16显卡一共16GB显存的服务器作为演示。部署的模型参数为14b的。如果需要部署满血版本671b的,需要更大的算力支持,这里由于是个人资金有限,就演示14b的部署…...
元学习之孪生网络Siamese Network
简介:元学习是一种思想,一般以神经网络作为特征嵌入的工具,实现对数据特征的提取,然后通过构造某种指标以引导优化器对模型参数进行优化。而最小化距离是最常见的学习目标,这就是熟知的度量学习,度量学习里…...

深入HBase——引入
引入 前面我们通过深入HDFS到深入MapReduce ,从设计和落地,去深入了解了大数据最底层的基石——存储与计算是如何实现的。 这个专栏则开始来看大数据的三驾马车中最后一个。 通过前面我们对于GFS和MapReduce论文实现的了解,我们知道GFS在数…...
Python创建FastApi项目模板
1. 项目结构规范 myproject/ ├── app/ │ ├── core/ # 核心配置 │ │ ├── config.py # 环境配置 │ │ └── security.py # 安全配置 │ ├── routers/ # 路由模块 │ │ └── users.py # 用户路由 │ ├…...

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

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

什么是计算机中的 “终端”?
在我们初学编程的时候,会遇到一个很重要的概念 ——终端。那它到底是什么呢? 在计算机领域,终端就像是我们和计算机进行对话的 “窗口”。我们可以在这个窗口里,用一些特定的命令来告诉计算机该做什么。比如,让计算机…...
LeetCode刷题---字符串---819
最常见的单词 819. 最常见的单词 - 力扣(LeetCode) 题目: 给你一个字符串 paragraph 和一个表示禁用词的字符串数组 banned ,返回出现频率最高的非禁用词。题目数据 保证 至少存在一个非禁用词,且答案 唯一 。 par…...

SSH IBM AIX服务器相关指标解读
(一)ZPU使用率 含义 在IBM AIX服务器中,ZPU使用率反映了特定处理单元(ZPU,假设是某种自定义或特定环境下的处理单元)的资源利用程度。它表示ZPU在一段时间内处于忙碌状态执行任务的时间比例。例如ÿ…...

Wireshark TS | 再谈虚假的 TCP Spurious Retransmission
前言 在之前的《虚假的 TCP Spurious Retransmission》文章中曾提到一个错误判断为 TCP Spurious Retransmission,实际为 TCP Out-Of-Order 的案例,本次继续探讨一个虚假的 TCP Spurious Retransmission 案例。 问题背景 TCP Spurious Retransmission…...
web vue 项目 Docker化部署
Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段: 构建阶段(Build Stage):…...
内存分配函数malloc kmalloc vmalloc
内存分配函数malloc kmalloc vmalloc malloc实现步骤: 1)请求大小调整:首先,malloc 需要调整用户请求的大小,以适应内部数据结构(例如,可能需要存储额外的元数据)。通常,这包括对齐调整,确保分配的内存地址满足特定硬件要求(如对齐到8字节或16字节边界)。 2)空闲…...

调用支付宝接口响应40004 SYSTEM_ERROR问题排查
在对接支付宝API的时候,遇到了一些问题,记录一下排查过程。 Body:{"datadigital_fincloud_generalsaas_face_certify_initialize_response":{"msg":"Business Failed","code":"40004","sub_msg…...
uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖
在前面的练习中,每个页面需要使用ref,onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入,需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...

抖音增长新引擎:品融电商,一站式全案代运营领跑者
抖音增长新引擎:品融电商,一站式全案代运营领跑者 在抖音这个日活超7亿的流量汪洋中,品牌如何破浪前行?自建团队成本高、效果难控;碎片化运营又难成合力——这正是许多企业面临的增长困局。品融电商以「抖音全案代运营…...
unix/linux,sudo,其发展历程详细时间线、由来、历史背景
sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...

微信小程序云开发平台MySQL的连接方式
注:微信小程序云开发平台指的是腾讯云开发 先给结论:微信小程序云开发平台的MySQL,无法通过获取数据库连接信息的方式进行连接,连接只能通过云开发的SDK连接,具体要参考官方文档: 为什么? 因为…...

(转)什么是DockerCompose?它有什么作用?
一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用,而无需手动一个个创建和运行容器。 Compose文件是一个文本文件,通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...
Hive 存储格式深度解析:从 TextFile 到 ORC,如何选对数据存储方案?
在大数据处理领域,Hive 作为 Hadoop 生态中重要的数据仓库工具,其存储格式的选择直接影响数据存储成本、查询效率和计算资源消耗。面对 TextFile、SequenceFile、Parquet、RCFile、ORC 等多种存储格式,很多开发者常常陷入选择困境。本文将从底…...

云原生安全实战:API网关Kong的鉴权与限流详解
🔥「炎码工坊」技术弹药已装填! 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、基础概念 1. API网关(API Gateway) API网关是微服务架构中的核心组件,负责统一管理所有API的流量入口。它像一座…...