【Linux旅行记】第一个小程序“进度条“!
文章目录
- 一、预备知识
- 1.1回车换行
- 1.2缓冲区
- 二、倒计时
- 三、进度条
- 3.1普通版本源代码
- 3.2高级版本源代码
- 🍀小结🍀
🎉博客主页:小智_x0___0x_
🎉欢迎关注:👍点赞🙌收藏✍️留言
🎉系列专栏:Linux入门到精通
🎉代码仓库:小智的代码仓库
一、预备知识
1.1回车换行
我们一般意义上的回车换行是两个概念,一般我们在C语言上的\n
的作用是回车+换行,他把两个操作都做了,但是\转义字符
也有别的的作用,我们一般回车是回车,换行是换行,两个是不同的概念。回车是指讲光标移动到当前行的最左侧开始位置,换行指的是光标水平方向保持不变,向下平移一行,C语言中有一个\r
就是实现回车功能。
我们平时用的键盘上面有一个Enter
的按键,这个按键就是实现了我们的回车+换行的作用。
1.2缓冲区
我们先来看一段代码:
#include "processBar.h"
#include <unistd.h>
int main()
{printf("hello world\n");sleep(2);return 0;
}
这个程序是输出一个hello world
然后再让程序休眠2秒,这里的sleep
函数是系统函数功能是让程序休眠指定时间,我们可以通过man 3 sleep
手册来查看sleep
函数的使用方法。
我们来运行这个代码来看看效果:
这里可以看到的现象是,程序先完成了打印hello world
再休眠两秒钟。
我们再来改改代码:
#include "processBar.h"
#include <unistd.h>
int main()
{printf("hello world");sleep(2);return 0;
}
这次我们把\n
去掉,此时程序会是怎么样的呢,先打印还是先休眠,我们一起来验证一下:
可以看到我们把\n
去掉之后程序是先休眠了两秒,接着再在显示器上打印出hello world
,这次没有换行符,所以bash
命令行就会紧接在hello world
的后面。
那么事实真的是这样的嘛?
我们可以来想一想,一个C语言程序是严格按照代码顺序从上往下依次执行的,不管怎样都是这样,肯定是先执行打印printf
在进行sleep
休眠2秒,但是此时又有一个疑问了,为什么我们printf
的内容没有显示出来呢?在我sleep
期间hello world
在哪里?
一定被保存起来了,那么要保存就一定需要内存空间,这里其实就是保存在来我们的缓冲区里面,这里的缓冲区是由C语言维护的一段内存
所以当程序执行结束的时候才会将缓冲区中的内容刷新出来。
那我们要强制刷新呢?
这里我们就要知道一个C语言程序运行会默认帮我们打开这三个流:
- stdin - - - -标准输入流(键盘)
- stdout - - - - 标准输出流(显示器)
- stderr - - - -标准错误流(显示器)
在我们平时使用文件操作的时候打开一个文件也是用的FILE *
类型来打开文件。
那么我们要强制刷新就要用到这个函数fflush
这里刚好就是接收FILE*
类型。
具体操作:
#include "processBar.h"
#include <unistd.h>
int main()
{printf("hello world");fflush(stdout);sleep(2);return 0;
}
我们其实打印出的数据是往stdout
中打印的,所以我们在printf
后面紧接着一个fflush
来强制刷新。
我们来运行看看现象:
可以看到这次我们是先显示出了hello warld
再休眠两秒,bash
的命令行才刷新出来。
总结:
\n
可以刷新缓冲区fflush
可以强制刷新缓冲区- 程序结束可以刷新缓冲区
二、倒计时
有了上面的基础我们可以简单的来实现一个倒计时:
#include "processBar.h"
#include <unistd.h>
int main()
{int cnt=10;//定义倒计时时间while(cnt>=0){printf("%-2d\r",cnt);//%-2d 表示以两个字符位输出并以左对齐方式输出 `\r`表示只回车不换行fflush(stdout);//强制刷新缓冲区cnt--;sleep(1);//程序休眠1s}printf("\n");return 0;
}
我们再来看看效果:
在这段代码中,%-2d\r
的作用是实现倒计时效果。%-2d
表示以两个字符位输出并以左对齐方式输出,%d
是输出整数的占位符。\r
表示回车到行首,即光标移到行首,而不换行。
因此,每次循环时,数字会被输出并覆盖上一次输出的数字,从而实现倒计时的效果。由于使用了\r
回车到行首,所以数字输出在同一行上,不会换行。
另外,fflush(stdout)
强制刷新缓冲区是为了确保每次输出都能够立即显示在屏幕上,而不是留在缓冲区中等待下一次输出。sleep(1)
函数是让程序休眠1秒钟,以便实现倒计时效果。
三、进度条
设计思路:
- 确定进度条的显示方式,例如使用字符 ‘#’ 表示进度条的进度。
- 确定进度条的长度,例如设定进度条长度为100个字符。
- 计算进度条的进度,例如已完成任务的百分比为 50%,则进度条应该显示50个 ‘#’ 字符。
- 每次更新进度条时,先将光标移动到行首,然后输出当前进度条的状态,再将光标移回到行首,以便下一次更新。
- 可以使用定时器或者其他方式控制进度条的更新速度,例如每隔1秒更新一次进度条。
3.1普通版本源代码
processBar.h
#pragma once
#include<stdio.h>
#define NUM 102 // 进度条长度
#define TOP 100 // 进度条最大值
#define BODY '=' // 进度条已完成部分的字符
#define RIGHT '>' // 进度条右边界的字符// 进度条函数的声明
extern void processbar(int speed);
processBar.c
#include "processBar.h"
#include <string.h>
#include <unistd.h>
// 进度条的四种状态,即 |、/、-、\
const char *lable="|/-\\";void processbar(int speed)
{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++]= BODY; // 将 BODY 字符加入到进度条数组中,并将当前值加1if(cnt<100) bar[cnt] = RIGHT; // 当进度条未达到100%时,在进度条的末尾加上 RIGHT 字符,防止有边界越界usleep(speed); // 程序休眠一段时间,以控制进度条的更新速度}printf("\n"); // 输出提示信息,任务已完成
}
main.c
#include "processBar.h"
#include <unistd.h>
int main()
{processbar(100000);return 0;
}
代码效果:
3.2高级版本源代码
在现实中进度条是表示我们下载某些文件的进度,所以进度不是由我们自己来决定的需要,所以我们需要写一个进度条接口来接收当前下载的百分比,进而通过调用函数来打印出当前的进度。
plus版本:
processBar.h
#pragma once
#include<stdio.h>
#define NUM 102 // 进度条长度
#define TOP 100 // 进度条最大值
#define BODY '=' // 进度条已完成部分的字符
#define RIGHT '>' // 进度条右边界的字符extern void processbar(int rate);
processBar.c
#include "processBar.h"
#include <string.h>
#include <unistd.h>// 进度条的四种状态,即 |、/、-、\
const char *lable="|/-\\";// 存储进度条的字符数组,初始化为0
char bar[NUM]={0};void processbar(int rate)
{if(rate < 0 || rate > 100) return; // 判断进度条的值是否在合法范围内int len = strlen(lable); // 计算进度条状态的长度printf("[%-100s][%d%%][%c]\r",bar,rate,lable[rate%len]); // 输出进度条信息fflush(stdout); // 刷新缓冲区,使得程序能够立即输出bar[rate++]= BODY; // 将 BODY 字符加入到进度条数组中,并将当前值加1if(rate<100) bar[rate] = RIGHT; // 当进度条未达到100%时,在进度条的末尾加上 RIGHT 字符,防止有边界越界添加
}
main.c
#include "processBar.h"
#include <unistd.h>
int main()
{int total = 1000;//要下载的总进度int curr = 0;//初始进度while(curr <= total){processbar(curr*100/total);curr+=10;//每次下载10usleep(50000);//模拟下载花费的时间}printf("\n");return 0;
}
plusplus版本:
processBar.h
#pragma once
#include<stdio.h>
#define NUM 102 // 进度条长度
#define TOP 100 // 进度条最大值
#define BODY '=' // 进度条已完成部分的字符
#define RIGHT '>' // 进度条右边界的字符// 进度条函数的声明
extern void processbar(int rate);
extern void initbar();
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="|/-\\";// 存储进度条的字符数组,初始化为0
char bar[NUM]={0};// 进度条函数的具体实现部分,实现了进度条的显示、刷新、更新等功能
void processbar(int rate)
{if(rate < 0 || rate > 100) return; // 判断进度条的值是否在合法范围内if(rate==0) initbar(); // 如果进度条为0,则初始化进度条数组int len = strlen(lable); // 计算进度条状态的长度printf("["LIGHT_BLUE"%-100s"NONE"]""[%d%%][%c]\r",bar,rate,lable[rate%len]); // 输出进度条信息,带有颜色fflush(stdout); // 刷新缓冲区,使得程序能够立即输出bar[rate++]= BODY; // 将 BODY 字符加入到进度条数组中,并将当前值加1if(rate<100) bar[rate] = RIGHT; // 当进度条未达到100%时,在进度条的末尾加上 RIGHT 字符,以便显示进度条的右边界
}// 初始化进度条数组
void initbar()
{memset(bar, '\0', sizeof(bar));
}
main.c
#include "processBar.h"
#include <unistd.h>// 定义了一个函数指针类型 callback_t
typedef void (*callback_t)(int);// 模拟一种安装或者下载的任务
void downLoad(callback_t cb)
{int total = 1000; // 总大小为1000MBint curr = 0; // 当前下载大小为0MBwhile(curr <= total){usleep(50000); // 模拟下载花费的时间int rate = curr*100/total; // 计算当前下载进度cb(rate); // 通过回调函数展示进度curr += 10; // 循环下载了一部分}printf("\n"); // 输出提示信息,任务已完成
}int main()
{printf("donwnload 1: \n");downLoad(processbar); // 下载任务1,回调函数为 processbarprintf("donwnload 2: \n");downLoad(processbar); // 下载任务2,回调函数为 processbarprintf("donwnload 3: \n");downLoad(processbar); // 下载任务3,回调函数为 processbarprintf("donwnload 4: \n");downLoad(processbar); // 下载任务4,回调函数为 processbarreturn 0;
}
上面的代码实现了一个简单的下载任务,并通过回调函数 processbar
实现了下载进度的显示。代码主要分为以下几个部分:
-
头文件部分,包含了
stdio.h
头文件和processBar.h
头文件,以及一些宏定义。 -
进度条函数的声明部分,声明了进度条函数
processbar
和初始化进度条数组的函数initbar
。 -
进度条函数的具体实现部分,实现了进度条的显示、刷新、更新等功能。这部分代码和之前相同。
-
初始化进度条数组的函数
initbar
的具体实现部分。这个函数只是简单地将进度条数组清零。 -
主函数部分,模拟了四个下载任务,并通过回调函数
processbar
展示下载进度。具体来说,这部分代码主要做了以下几件事情:-
调用
downLoad
函数模拟四个下载任务,并将回调函数设置为processbar
。 -
在每个下载任务开始时输出提示信息。
-
在每个下载任务结束时输出提示信息。
-
代码运行效果:
🍀小结🍀
今天我们学习了"Linux进度条小程序"
相信大家看完有一定的收获。种一棵树的最好时间是十年前,其次是现在!
把握好当下,合理利用时间努力奋斗,相信大家一定会实现自己的目标!加油!创作不易,辛苦各位小伙伴们动动小手,三连一波💕💕~~~
,本文中也有不足之处,欢迎各位随时私信点评指正!
相关文章:

【Linux旅行记】第一个小程序“进度条“!
文章目录 一、预备知识1.1回车换行1.2缓冲区 二、倒计时三、进度条3.1普通版本源代码3.2高级版本源代码 🍀小结🍀 🎉博客主页:小智_x0___0x_ 🎉欢迎关注:👍点赞🙌收藏✍️留言 &…...

DeepMind将AI用于可控核聚变:将等离子体形状模拟精度提高65%
近日,英国AI公司DeepMind宣布取得了一项新的突破,成功实现了AI可控核聚变。这一技术能够在高温等离子体环境下实现精准放电,为核聚变技术的发展提供了新的思路和创新。 长期以来,相关领域的科学家们,一直在寻找清洁、取…...

Scrum是什么意思,Scrum敏捷项目管理工具有哪些?
一、什么是Scrum? Scrum是一种敏捷项目管理方法,旨在帮助团队高效地开展软件开发和项目管理工作。 Scrum强调迭代和增量开发,通过将项目分解为多个短期的开发周期(称为Sprint),团队可以更好地应对需求变…...
【从零单排Golang】第十三话:使用WaitGroup等待多路并行的异步任务
在后端开发当中,经常会遇到这样的场景:请求给了批量的输入,对于每一个输入,我们都要给外部发请求等待返回,然后才能继续其它自己的业务逻辑。在这样的case下,如果每一个输入串行处理的话,那么很…...

WSL2安装CentOS7和CentOS8
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、下载ZIP包?二、安装1.打开Windows子系统支持2.安装到指定位置3.管理虚拟机4.配置虚拟机1.配置国内源2.安装软件3.安装第三方源 5.配置用户1.创建…...

不平衡电网条件下基于变频器DG操作的多目标优化研究(Matlab代码Simulink实现)
目录 💥1 概述 📚2 运行结果 🎉3 参考文献 🌈4 Matlab代码&Simulink实现&文章讲解 💥1 概述 文献来源: 最近,利用并网转换器(GCC)克服电网故障并支撑电网电压已…...
【Leetcode】(自食用)简单题||单词数
step by step. 题目: 统计字符串中的单词个数,这里的单词指的是连续的不是空格的字符。 请注意,你可以假定字符串里不包括任何不可打印的字符。 示例: 输入: "Hello, my name is John" 输出: 5 解释: 这里的单词是指连续的不是空格…...

C语言代码的x86-64汇编指令分析过程记录
先通过Xcode创建一个terminal APP,语言选择C。代码如下: #include <stdio.h>int main(int argc, const char * argv[]) {int a[7]{1,2,3,4,5,6,7};int *ptr (int*)(&a1);printf("%d\n",*(ptr));return 0; } 在return 0处打上断点&…...

基于springboot+vue的房屋租赁系统(前后端分离)
博主主页:猫头鹰源码 博主简介:Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容:毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 项目介绍…...
Python文件读写操作详解:从基础到高级
摘要:文件读写是Python编程中常见的操作之一。本文将介绍Python中文件读写的基础知识,包括打开文件、读取文件内容、写入文件、关闭文件等基本操作。此外,还将探讨一些高级文件读写技术,如使用上下文管理器、处理异常、使用with语…...

ThreadLocal基本介绍
文章目录 什么是ThreadLocalThreadLocal解决了什么问题ThreadLocal的作用 ThreadLocal的使用场景ThreadLocal的代码示例ThreadLocal的优点ThreadLocal的缺点与volatile、synchronized、ThreadLocal比较 总结 什么是ThreadLocal ThreadLocal是Java中的一个线程本地变量…...

ffmpeg源码编译成功,但是引用生成的静态库(.a)报错,报错位置在xxx_list.c,报错信息为某变量未定义
背景:本文是对上一个文章的补充,在源码编译之前,项目是有完整的ffmpeg编译脚本的,只不过新增了断点调试ffmpeg,所以产生的上面的文章,也就是说,我在用make编译成功后,再去做的源码编…...

2023爱分析·信创云市场厂商评估报告:中国电子云
01 研究范围定义 信创2.0时代开启,信创进程正在从局部到全面、从细分到所有领域延展。在这个过程中,传统的系统集成,也在逐步向信创化、数字化及智能化转变。随着信创产业的发展,企业需要更多的技术支持和服务,而传统的系统集成已…...
网络安全学习笔记——XFF攻击流程
手工注入 手动报错注入,填写格式如:X-Forwarded-For: and updatexml(1,concat(0x7e,(select database()),0x7e),1) or 11 库名 1 and updatexml(1,concat(0x7e,database(),0x7e),1), 表名 1 and updatexml(1,concat(0x7e,(select table_name from…...
微信小程序阻止用户返回上一页,并弹窗给用户确定是否要返回上一页
在onload中调用微信的enableAlertBeforeUnload方法,在首次进入会自动监听当前的页面,在返回的时候会自动弹出弹窗阻止用户返回上一页,点击确定则返回上一页,取消则停留在当前页 onLoad: function(){wx.enableAlertBeforeUnload({…...

LangChain+ChatGLM整合LLaMa模型(二)
开源大模型语言LLaMa LLaMa模型GitHub地址添加LLaMa模型配置启用LLaMa模型 LangChainChatGLM大模型应用落地实践(一) LLaMa模型GitHub地址 git lfs clone https://huggingface.co/huggyllama/llama-7b添加LLaMa模型配置 在Langchain-ChatGLM/configs/m…...

【NLP】训练chatglm2的评价指标BLEU,ROUGE
当进行一定程度的微调后,要评价模型输出的语句的准确性。由于衡量的对象是一个个的自然语言文本,所以通常会选择自然语言处理领域的相关评价指标。这些指标原先都是用来度量机器翻译结果质量的,并且被证明可以很好的反映待评测语句的准确性&a…...

java+springboot+mysql员工工资管理系统
项目介绍: 使用javaspringbootmysql开发的员工工资管理系统,系统包含超级管理员,系统管理员、员工角色,功能如下: 超级管理员:管理员管理;部门管理;员工管理;奖惩管理&…...

FL Studio Producer Edition 21 v21.0.3 Build 3517 Windows/mac官方中文版
FL Studio Producer Edition 21 v21.0.3 Build 3517 Windows FL Studio Producer Edition 21 v21.0.3 Build 3517 Windows/mac官方中文版是一个完整的软件音乐制作环境或数字音频工作站(DAW)。它代表了 25 多年的创新发展,将您创作、编曲、录…...

探索Python数据容器之乐趣:列表与元组的奇妙旅程!
文章目录 零 数据容器入门一 数据容器:list(列表)1.1 列表的定义1.2 列表的下表索引1.3 列表的常用操作1.3.1 列表的查询功能1.3.2 列表的修改功能1.3.3 列表常用方法总结 1.4 补充:append与extend对比1.5 list(列表)的遍历1.6 补…...
基于算法竞赛的c++编程(28)结构体的进阶应用
结构体的嵌套与复杂数据组织 在C中,结构体可以嵌套使用,形成更复杂的数据结构。例如,可以通过嵌套结构体描述多层级数据关系: struct Address {string city;string street;int zipCode; };struct Employee {string name;int id;…...

使用VSCode开发Django指南
使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架,专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用,其中包含三个使用通用基本模板的页面。在此…...
多场景 OkHttpClient 管理器 - Android 网络通信解决方案
下面是一个完整的 Android 实现,展示如何创建和管理多个 OkHttpClient 实例,分别用于长连接、普通 HTTP 请求和文件下载场景。 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas…...

Linux相关概念和易错知识点(42)(TCP的连接管理、可靠性、面临复杂网络的处理)
目录 1.TCP的连接管理机制(1)三次握手①握手过程②对握手过程的理解 (2)四次挥手(3)握手和挥手的触发(4)状态切换①挥手过程中状态的切换②握手过程中状态的切换 2.TCP的可靠性&…...
测试markdown--肇兴
day1: 1、去程:7:04 --11:32高铁 高铁右转上售票大厅2楼,穿过候车厅下一楼,上大巴车 ¥10/人 **2、到达:**12点多到达寨子,买门票,美团/抖音:¥78人 3、中饭&a…...

使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台
🎯 使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台 📌 项目背景 随着大语言模型(LLM)的广泛应用,开发者常面临多个挑战: 各大模型(OpenAI、Claude、Gemini、Ollama)接口风格不统一;缺乏一个统一平台进行模型调用与测试;本地模型 Ollama 的集成与前…...
LeetCode - 199. 二叉树的右视图
题目 199. 二叉树的右视图 - 力扣(LeetCode) 思路 右视图是指从树的右侧看,对于每一层,只能看到该层最右边的节点。实现思路是: 使用深度优先搜索(DFS)按照"根-右-左"的顺序遍历树记录每个节点的深度对于…...

数据结构:泰勒展开式:霍纳法则(Horner‘s Rule)
目录 🔍 若用递归计算每一项,会发生什么? Horners Rule(霍纳法则) 第一步:我们从最原始的泰勒公式出发 第二步:从形式上重新观察展开式 🌟 第三步:引出霍纳法则&…...
深度解析:etcd 在 Milvus 向量数据库中的关键作用
目录 🚀 深度解析:etcd 在 Milvus 向量数据库中的关键作用 💡 什么是 etcd? 🧠 Milvus 架构简介 📦 etcd 在 Milvus 中的核心作用 🔧 实际工作流程示意 ⚠️ 如果 etcd 出现问题会怎样&am…...
基于Java项目的Karate API测试
Karate 实现了可以只编写Feature 文件进行测试,但是对于熟悉Java语言的开发或是测试人员,可以通过编程方式集成 Karate 丰富的自动化和数据断言功能。 本篇快速介绍在Java Maven项目中编写和运行测试的示例。 创建Maven项目 最简单的创建项目的方式就是创建一个目录,里面…...