【Linux修行路】进度条小程序
目录
⛳️推荐
一、预备知识
1.1 回车换行
1.2 缓冲区
二、倒计时
2.1 注意事项
三、进度条
3.1 源代码
3.2 代码分析
3.2 实际使用场景
⛳️推荐
前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站
一、预备知识
1.1 回车换行
一般意义上的回车换行是两个独立的独立的动作,而C语言中的\n
则同时完成了回车和换行的工作。回车是将光标移动到当前行的做开始(最左侧),换行是将光标水平方向保持不变,竖直方向向下平移一行。C语言中可以通过转义字符\r
实现回车。
我们电脑键盘上的EBTER
按键则是同时实现了回车和换行的功能,当按下ENTER
键,光标会去到下一行的最开始的位置。
1.2 缓冲区
📖先看一个现象
#include <unistd.h>int main()
{printf("Hello Linux!\n"); sleep(2);return 0;
}
这段代码很简单,现在屏幕上打印出Hello Linux!
,接着调用sleep
函数让程序休眠两秒。接下来,我们对上面的代码稍作修改,去掉\n
再来试试。
#include <unistd.h>int main()
{printf("Hello Linux!"); sleep(2);return 0;
}
通过动图可以看到,在去掉/n
后对代码编译运行,先是休眠了两秒,接着才在屏幕上打印出Hello Linux!
,并且因为没有\n
,所以打印完后没有换行,导致bash
命令行就紧跟在打印结果的后面。
📖现象分析
很多小伙伴会根据上面的现象猜测,这段代码先执行了sleep
休眠,再去执行printf
打印,这样的猜测是错误的,因为任何一个C程序,都是严格按照代码的顺序去执行。既然这样的话,先执行printf
,再执行sleep
,那在休眠的两秒期间,printf
的打印结果在哪里呢?由于最终Hello Linux!
还是出现在我们的屏幕上,所以在这两秒期间,Hello Linux
一定是被保存起来了,其实就是保存在缓冲区中。缓冲区就是C语言维护的一段内存。默认当程序结束的时候才会将缓冲区中的内容刷新出来。
📖如何强制刷新缓冲区
任何一个C程序运行的时候都会默认帮我们打开以下三个流:
- stdin - - - - 标准输入流(键盘)
- stdout - - - - 标准输出流(显示器)
- stderr - - - - 标准错误(显示器)
Linux下一切介文件,这三个流都是FILE*
的指针,所以任何一个C程序运行的时候,操作系统会帮我们打开以上三个文件。今天我们只需要关心stdout
标准输出流即可。我们可以通过fflush
函数来刷新缓冲区。
#include <unistd.h>int main()
{printf("Hello Linux!"); fflush(stdout);//刷新缓冲区 sleep(2);return 0;
}
通过运行结果可以看出,这一次虽然在打印的时候也没有加\n
,但取先把Hello Linux!
打印出来,然后再休眠两秒。
小Tips:通过上面的分析我们可以得出,刷新缓冲区主要有以下几种方法:
\n
可以刷新缓冲区。- 程序结束也会刷新缓冲区。
fflush(stdout)
可以手动刷新缓冲区。
二、倒计时
有了上面的知识储备,我们先来实现一个简单的倒计时练练手。
📖源代码
#include "processBar.h"
#include <unistd.h>
int main()
{int cnt = 10;while(cnt >= 0){printf("%-2d\r",cnt);fflush(stdout);sleep(1);cnt--;}printf("\n"); return 0;
}
📖效果演示
2.1 注意事项
📖回车、刷新缓冲区
由于倒计时,是用新数字去覆盖老数字,因此每打印一个数字后不能用\n
进行换行,否则就会像下面这样:
这里的正确做法是,每打印一个数字后紧跟着打印一个\r
回车,让光标回到这一行最开始的位置,这样新打印的数字就会去覆盖掉老的数字。但是\r
不会去刷新缓冲区,因此在每打印完一个数字后,需要调用fflush(stdout)
来刷新缓冲区。
📖格式化控制
这里我们需要知道,往显示器上打印整型10,本质上是打印了字符1
和字符0
,由于这两个字符是挨在一起的,我们看起来就像是整型10。因此打印10,会占用两个字符,而打印0~9只需要一个字符,所以\r
回车之后去覆盖写,只会覆盖一个字符,对第二个字符0
始终没有影响,因此我们需要用%-2d
来控制,每次打印两个位宽的字符,-
表示将这两个字符左对齐。如果不进行格式化控制,打印出来的结果将是下面这样:
三、进度条
3.1 源代码
📖processBar.h
#pragma once
#include <stdio.h>
#define NUM 102
#define STYLE '='
#define TOP 100
#define BODY '>'
extern void processbar();
📖processBar.c
#include "processBar.h"
#include <string.h>
#include <unistd.h>
const char* lable = "|/-\\";//旋转提示
void processbar()
{char bar[NUM];memset(bar, '\0', sizeof(bar));int len = strlen(lable);int cnt = 0;while(cnt <= TOP){printf("[%-100s][%d%%][%c]\r", bar, cnt, lable[cnt%len]);fflush(stdout);bar[cnt++] = STYLE;if(cnt < 100){bar[cnt] = BODY; }usleep(100000);//以微秒为单位进行休眠,想让进度条10秒跑完,因为一共会循环101次,所以每次循环大概就是休眠0.1秒,100毫秒,10000微秒}printf("\n");
}
📖效果演示
3.2 代码分析
📖进度条往右走的实现原理
进度条向右走动的原理就是,这一次比上一次多打印一点内容。因此我们可以定义一个字符数组bar
,通过循环每次往字符数组里面追加字符,然后将这个字符数组打印出来,由于每次循环都会往数组里追加字符,所以就会导致下一次打印出来的内容比这一次的多,视觉上就感觉进度条在往右走。又因为进度条始终是在同一行往右走的,所以每打印完一次要用\r
,让光标回到当前行的最开始位置,下一次打印就会产生覆盖的效果。其次是进度条的风格,这里我们定义了标识符常量STYLE
来表示进度条的风格。
📖while循环逻辑分析
因为进度条是从0~100%,中间有101个跨度,因此循环的次数就是101次,因此cnt
的范围是[0,100]
,这里用TOP
来表示区间的右端点100。整个循环会执行101次打印动作和101次字符追加动作,因为总共会追加101个字符,再加上末尾的\0
,一共就是102个字符,因此表示数组大小的NUM
就是102。最初将数组中的内容全部初始化为\0
,这样,第一次打印的就是一个空串什么也没有,对标0%,打印完后进行追加,在数组下标为cnt
的位置(也就是下标为0的位置)追加了一个=
,下标为cnt+1
的位置(也就是下标为1的位置)追加一个>
,第二次打印出来的就是=>
,对标1%。当到进度到达100%的时候,我们希望打印出来的进度条右边没有>
,因为100%对应的是最后一次打印,也就是当cnt == 100
的时候,此时我们希望打印出100个=
即可,这意味着,当执行这次打印时,数组下标为99的位置存储的是一个=
并且下标为100的位置是\0
,前者简单,当cnt == 99
的时候字符串追加的时候会把其设置成=
,要满足后者,我们就要加一个判断条件当cnt < 100
的时候才能将bar[cnt]
设置成>
,否则不能修改bar[cnt]
。
3.2 实际使用场景
上面的processBar.c
中为了演示进度条的原理,在里面写了一个while
循环来模拟,但实际上的进度条并不是这样用的。以下载东西为例,作为一个进度条,它本身并不知道下载了多少,它只会提供一个接口,在下载东西的时候,调用这个接口,然后将已经下载好的比率作为参数传给进度条模块,它会根据比率打印出对应的进度条样式。
📖版本一
//processBar.h
#pragma once
#include <stdio.h>
#define NUM 102
#define STYLE '='
#define TOP 100
#define BODY '>'
extern void processbar(int ret);
//processBar.c
#include "processBar.h"
#include <string.h>
#include <unistd.h>
const char* lable = "|/-\\";
//V2版本
char bar[NUM] = {'\0'};//定义在全局避免每一次函数调用都会重现创建
void processbar(int ret)
{if(ret <0 || ret > 100)//合理性判断{return;}if(ret == 0)//当比率为0的时候将数组全置为'\0'{memset(bar, '\0', sizeof(bar));}int len = strlen(lable);printf("[%-100s][%d%%][%c]\r", bar, ret, lable[ret%len]);fflush(stdout);bar[ret++] = STYLE;if(ret < 100){bar[ret] = BODY;}
}
//main.c
int main()
{ int total = 1000;//假设总共要下载1000个G int cur = 0;//当前下载的 while(cur <= total) { processbar(cur * 100 / total); usleep(50000);//模拟下载花费时间 cur += 10;//循环下载了一部分,更新进度 } return 0;
}
📖版本二
//processBar.h
#pragma once
#include <stdio.h>
#define NUM 102
#define STYLE '='
#define TOP 100
#define BODY '>'
extern void processbar(int ret);
//processBar.c
#include "processBar.h"
#include <string.h>
#include <unistd.h>
#define NONE "\033[m"
#define RED "\033[0;32;31M"
#define GREEN "\033[0;32;32m"
#define LIGHT_BLUE "\033[1;34m"
#define LIGHT_PURPLE "\033[1;35m"
const char* lable = "|/-\\";
//V2版本
char bar[NUM] = {'\0'};
void processbar(int ret)
{if(ret <0 || ret > 100)//合理性判断{return;}if(ret == 0)//当比率为0的时候将数组全置为'\0'{memset(bar, '\0', sizeof(bar));}int len = strlen(lable);printf("["LIGHT_BLUE"%-100s"NONE"]""[%d%%][%c]\r", bar, ret, lable[ret%len]); fflush(stdout); bar[ret++] = STYLE;if(ret < 100){bar[ret] = BODY;}
}
//main.c
#include "processBar.h"
#include <unistd.h> typedef void (*callback_t) (int);
//模拟一种安装或者下载
void Downbload(callback_t ct)
{ int total = 1000;//假设总共要下载1000个MB int cur = 0;//当前下载的 while(cur <= total) { int rate = cur*100/total; ct(rate); usleep(50000);//模拟下载花费时间 cur += 10;//循环下载了一部分,更新进度 } printf("\n");
} int main()
{ printf("Downbload 1:\n"); Downbload(processbar); printf("Downbload 2:\n"); Downbload(processbar); printf("Downbload 3:\n"); Downbload(processbar); printf("Downbload 4:\n"); Downbload(processbar);return 0;
}
📖效果演示
🎁结语:
今天的分享到这里就结束啦!如果觉得文章还不错的话,可以三连支持一下,您的支持就是我前进的动力!
相关文章:
【Linux修行路】进度条小程序
目录 ⛳️推荐 一、预备知识 1.1 回车换行 1.2 缓冲区 二、倒计时 2.1 注意事项 三、进度条 3.1 源代码 3.2 代码分析 3.2 实际使用场景 ⛳️推荐 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家…...
网络安全入门教程(非常详细)从零基础入门到精通,看完这一篇就够了。
学前感言: 1.这是一条坚持的道路,三分钟的热情可以放弃往下看了.2.多练多想,不要离开了教程什么都不会了.最好看完教程自己独立完成技术方面的开发.3.有时多google,baidu,我们往往都遇不到好心的大神,谁会无聊天天给你做解答.4.遇到实在搞不懂的,可以先放放,以后再来解决. 基…...
【探索Linux】P.44(数据链路层 —— 以太网的帧格式 | MAC地址 | MTU | ARP协议)
阅读导航 引言一、认识以太网二、以太网的帧格式三、MAC地址四、MTU五、ARP协议温馨提示 引言 在深入探讨了网络层的IP协议之后,本文将带领读者进一步深入网络的底层——数据链路层。我们将详细解析以太网的帧格式,这是数据链路层传输数据的基本单元&am…...
<数据集>航拍行人识别数据集<目标检测>
数据集格式:VOCYOLO格式 图片数量:7482张 标注数量(xml文件个数):7482 标注数量(txt文件个数):7482 标注类别数:1 标注类别名称:[people, pedestrian] 序号类别名称图片数框数1people5226385602pedes…...
在 Windows 10 系统上部署 Medusa
先决条件 在安装 Medusa 之前,你需要确保已经安装了以下工具: Node.js: Medusa 需要 Node.js v16 或更高版本。你可以从 Node.js 官网下载并安装。Git: Git 用于从 GitHub 获取 Medusa 的源代码。你可以从 Git 官网下载并安装。PostgreSQL: Medusa 使用…...
Linux进程 (冯诺依曼体结构 管理 PCB 进程状态 僵尸进程 孤儿进程 运行阻塞挂起状态 进程优先级)
文章目录 一.冯诺依曼体系结构冯诺依曼结构能干什么? 二.操作系统概念结构图(不完整)为什么要有操作系统? 尝试理解操作系统管理结构图(完整)总结: 三.进程进程是什么?PCB为什么要有PCB? Linux中的PCB进程的task_struc…...
《LlamaIndex 之美》-01-LLM、Prompt、Embedding基础入门
在基于数据构建任何 LLM 应用程序时,选择合适的大型语言模型 (LLM) 是您需要考虑的首要步骤之一。 LLM 是 LlamaIndex 的核心组成部分。它们可以作为独立模块使用,也可以插入到其他核心 LlamaIndex 模块(索引、检索器…...
C++ 智能指针简单介绍及用法
C 智能指针简单介绍及用法 智能指针是 C11 引入的一个非常实用的特性,旨在自动管理动态分配的内存,避免内存泄漏和悬空指针问题。主要有三种类型的智能指针:std::unique_ptr、std::shared_ptr 和 std::weak_ptr。下面是对它们的详细介绍&…...
k8s笔记之创建Istio Gateway规则
创建Istio Gateway 背景如何创建Istio Gateway规则配置方式rewrite重写路径直接去除match,默认都转发到一个服务路由规则多种配置方式实践(即开头的完整版) 涉及的命令补充注意事项 背景 为什么需要使用到Istio Gateway?充当k8s服…...
NAND行业回归盈利:AI与云存储需求驱动
市场概览 根据Yole Group于2024年6月25日发布的市场报告,经过五个季度的亏损之后,NAND闪存行业在2024年第一季度(1Q24)实现了盈利回归。这一转变主要得益于企业级固态硬盘(SSD)领域的强劲需求增长…...
【限免】频控阵雷达:概念、原理与应用【附MATLAB代码】
微信公众号:EW Frontier QQ交流群:949444104 主要内容 PDA、FDA MATLAB代码 %---------------------------------------- %功能:FDA和相控阵天线方向图 %版本:ver1.0 %时间:2017.11.1 %--------------------------------------- clear all; clc; disp…...
从0开始搭建vue + flask 旅游景点数据分析系统( 六):搭建后端flask框架
这一期开始开发header部分,预期实现两个目标: 创建 Flask 项目导入旅游数据后端实现旅游数据的查询 1 python 环境 & 开发环境 python 安装和pycharm安装需要去网上找包,建议python使用3.8 或者3.9版本 2 新建项目 我们新建一个文件…...
学习硬件测试04:触摸按键+PWM 驱动蜂鸣器+数码管(P62~P67、P71、P72)
一、触摸按键 1.1理论讲解 1.1.1实验现象 触摸按键 1 单击与长按,控制 LED1;触摸按键 2 单击与长按,控制 LED2;触摸按键 3 单击与长按,控制 LED3;触摸按键 4 单击与长按,控制继电器; 1.1.2硬件电路 是原理图上触摸…...
JS原型链
JS的原型链 文章目录 JS的原型链前言一、原型是什么?二、原型链总结 前言 在使用数组或对象中的方法时,你是不是会感觉很奇怪,为什么仅仅是创建了一个数组或是对象,就能够使用它提供的方法呢?JS是怎么做到的呢&#x…...
《Java初阶数据结构》----5.<二叉树的概念及使用>
前言 大家好,我目前在学习java。之前也学了一段时间,但是没有发布博客。时间过的真的很快。我会利用好这个暑假,来复习之前学过的内容,并整理好之前写过的博客进行发布。如果博客中有错误或者没有读懂的地方。热烈欢迎大家在评论区…...
git查看记录详解
文章目录 git查看记录查看文件修改列表查看修改差异友好的查看修改记录结合多个选项查看记录示例输出 git查看记录 使用 git log 你不仅可以查看提交记录,还可以通过一些选项查看文件的修改列表、修改差异,并以更友好的方式查看修改记录。以下是一些常用…...
检索增强生成RAG系列10--RAG的实际案例
讲了很多理论,最后来一篇实践作为结尾。本次案例根据阿里云的博金大模型挑战赛的题目以及数据集做一次实践。 完整代码地址:https://github.com/forever1986/finrag.git 本次实践代码有参考:https://github.com/Tongyi-EconML/FinQwen/ 目录 …...
程序员自我提升的全面指南
程序员自我提升的全面指南 1. 技术基础巩固重要性实践方法 2. 技术栈拓展重要性实践方法 3. 软技能提升重要性实践方法 4. 实践与项目经验重要性实践方法 5. 持续学习与职业规划重要性实践方法 6. 代码质量与优化重要性实践方法 7. 思维与创新能力重要性实践方法 8. 健康与心理…...
【golang】Golang手写元组 tuple | golang tuple
Golang手写元组 tuple 1、源码 如下: package tupletype Tuple[T any, U any] struct {First TSecond U }// zip combines elements of two slices into a slice of pairs (tuples), which is useful for combining related data. func Zip[T any, U any](slice…...
golang中struct的tag -简记
今天 简单整理一下,关于golang中struct的tag type User struct {UId int gorm:"column:uid;type:bigint;unique_index;not null;comment:用户id"Name string json:"name"Age int bson:"age"From string binding:"requi…...
分布式领域扩展点设计稿
分布式领域扩展点设计稿 背景坐标设计理念设计图Quick Start相关组件 背景 随着交易业务和基础知识的沉淀,愈发觉得扩展点可以在大型交易分布式架构中可以做更多的事情。 经过一个月的思考,决定将 单点领域扩展点(savior-ext) 从…...
玩转微信公众号变现:从新手到专家的全攻略
个人名片 🎓作者简介:java领域优质创作者 🌐个人主页:码农阿豪 📞工作室:新空间代码工作室(提供各种软件服务) 💌个人邮箱:[2435024119qq.com] 📱…...
JVM: 方法调用
文章目录 一、介绍二、方法调用的原理1、静态绑定2、动态绑定(1)介绍(2)原理 一、介绍 在JVM中,一共有五个字节码指令可以执行方法调用: invokestatic: 调用静态方法。invokespecial:调用对象…...
测试面试宝典(四十一)—— 接口自动化的优缺点
接口自动化测试的优点: 1. 提高测试效率:能够快速执行大量的测试用例,节省了手动测试重复执行的时间和人力成本。 2. 尽早发现问题:可以在软件开发的早期阶段介入,有助于及时发现接口层面的缺陷,降低修复…...
“火炬科企对接”先进计算产业推进会 | 麒麟信安受邀参加,并签署开源生态合作协议
7月30日,“火炬科企对接”先进计算产业推进会在长沙隆重召开。大会由工业和信息化部火炬高技术产业开发中心、湖南省科学技术厅、湖南省工业和信息化厅、湖南湘江新区管理委员会、中国邮政储蓄银行联合举办。麒麟信安与来自国内先进计算领域的专家学者,2…...
中文网址导航模版HaoWa1.3.1/模版网站wordpress导航主题
HaoWa v1.3.1由挖主题开发的一款网址导航类主题。 HaoWA主题除主体导航列表外,对主题所需的小模块都进行了开放式的HTML编辑器形式的功能配置,同时预留出默认的代码结构,方便大家在现有的代码结构上进行功能调整。 同时加入了字体图标Font …...
图欧学习资源网创站以来的更新日志(截止至2022.5.6)不完全统计
一、网站创立和初步发展 2021年10月28日创建【TUO图欧视频备份站】,当时只有【单词视频】文件夹. 小学、初中、高中、大学、四六级、专四、专八、专升本、考研、考博、托福、雅思、托业、GRE、SAT、GMAT、MBA、新概念等 数量:500个文件,60个…...
现代前端架构介绍(第二部分):如何将功能架构分为三层
远离JavaScript疲劳和框架大战,了解真正重要的东西 在这个系列的前一部分 《App是如何由不同的构建块构成的》中,我们揭示了现代Web应用是由不同的构建块组成的,每个构建块都承担着特定的角色,如核心、功能等。在这篇文章中&#…...
LeetCode Easy|【21. 合并两个有序链表】
力扣题目链接 状态:拿到本题的第一反应就是使用双指针,分别指向两个链表的开头位置。 随后的思路就是以第一条链表为基准完成插入,并且对于遍历到的每个节点都应该保存其状态。 写了一下代码后发现,我们应该以第一个节点较小的链表…...
大模型的架构参数是指定义模型基本结构和组成的各种参数,这些参数对模型的性能、训练效率和泛化能力具有重要影响。以下是对大模型架构参数的详细介绍
大模型架构参数 大模型的架构参数是指定义模型基本结构和组成的各种参数,这些参数对模型的性能、训练效率和泛化能力具有重要影响。以下是对大模型架构参数的详细介绍: 一、基本结构和组成 层数:模型的层数是指模型中全连接网络或特定结构…...
个人网站建设方案书 备案/班级优化大师app下载学生版
沉积式样式(透明) ios: 打开应用的manifest.json文件,切换到代码视图,在plus -> distribute -> apple 下添加UIReserveStatusbarOffset节点并设置值为false。 注意: 1. 真机运行不生效,需…...
wordpress彩色字体/游戏推广平台
参考资料: 1. 《Python基础教程》 2. http://www.runoob.com/python/python-chinese-encoding.html 3. http://www.liaoxuefeng.com/wiki/001374738125095c955c1e6d8bb493182103fac9270762a000 ▶ 中文编码 Python中默认的编码格…...
网站去哪做/seo站长工具平台
cpu缓存 一级缓存:每刻cpu核心有两个一级缓存,分别是L1P(progame程序)和L1D(data数据),分别用作程序的缓存和数据的缓存 二级缓存:核心级别的缓存,每颗核心独占的缓存 三级缓存:cpu级别的缓存,…...
宣传旅游网站建设的重点是什么/北京seo公司wyhseo
原文地址:[url]http://wakan.blog.51cto.com/59583/7232[/url](本文共分三部分,现在打开的是《第三部分》,欢迎继续阅读《第一部分》和《第二部分》)4 思想,无招胜有招 4.1 阶段释义 客户:我们…...
企业大型网站开发/淘宝代运营公司十大排名
打包_assets 需要引入2个加载器模块 url-loader: url-loader 功能类似于 file-loader,但是在文件大小(单位 byte)低于指定的限制时,可以返回一个 DataURL(base64字符串)file-loader: 产出, 寻找文件的引用位置 准备工作: 注意打包…...
2018网站内容和备案/太原互联网推广公司
一. 环境准备 1.1 主库环境(172.168.18.201) 环境 说明 查看脚本 操作系统版本 CentOS Linux release 7.4.1708 (Core) cat /etc/redhat-release 操作系统用户名和密码 root js*2015 IP地址 172.168.18.201 ip addr 网关Gateway 172.168.18.1 cat /etc/syscon…...