【Linux修行路】进度条小程序

news2024/9/23 15:32:11

图片

目录

⛳️推荐

一、预备知识

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;
}

📖效果演示

🎁结语:

        今天的分享到这里就结束啦!如果觉得文章还不错的话,可以三连支持一下,您的支持就是我前进的动力!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1976574.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

安卓基本布局(上)

文章目录 LinerLayout线性布局RelativeLayout相对布局根据父容器定位根据兄弟组件定位margin偏移padding填充 LinerLayout线性布局 以水平或垂直的方式来排列界面中的控件。 常用属性详细描述orientation布局中组件的排列方式。horizonta&#xff1a;水平&#xff1b;vertical…

GPT-4o mini模型:小型化AI解决方案的创新应用案例

文章目录 每日一句正能量前言开发者视角初步接触与性能评估集成与开发流程成本效益分析创新应用案例面临的挑战与解决方案社区贡献与经验分享未来展望 性能评估处理能力与响应速度准确性与可靠性多功能性与灵活性资源效率可扩展性与集成性用户定制与微调 结论 成本效益分析初始…

C语言基础知识之函数指针和指针函数

函数指针和指针函数 函数指针和指针函数指向函数的指针返回指针值的函数指针函数和函数指针的区别 问题1_1代码1_1结果1_1 函数指针和指针函数 指向函数的指针 用函数指针变量调用函数 可以用指针变量指向整型变量、字符串、数组&#xff0c;也可以指向一个函数。一个…

ctfshow-web入门-sql注入(web176-web180)

目录 1、web176 2、web177 3、web178 4、web179 5、web180 1、web176 1 order by 4-- 闭合后简单判断了下字段数是 3 测试联合查询注入&#xff0c;存在关键字的过滤&#xff0c;包括 select 和 union &#xff08;后面经过测试实际只过滤了 select&#xff09; 大小写绕…

常⻅CMS漏洞

常⻅CMS漏洞 ⼀&#xff1a;WordPress ​ WordPress是⼀个以PHP和MySQL为平台的⾃由开源的博客软件和内容管理系统。WordPress具 有插件架构和模板系统。截⾄2018年4⽉&#xff0c;排名前1000万的⽹站中超过30.6%使⽤WordPress。 WordPress是最受欢迎的⽹站 内容管理系统。全…

Linux网络之多路转接——实用的epoll

目录 一、高级IO 1.1 概念 1.2 五种IO模型 1.3 小结 二、多路转接的实用派 2.1 epoll 接口 2.1.1 epoll_create 2.1.2 epoll_ctl 2.1.3 epoll_wait 2.2 epoll 底层原理 2.2.1 epoll_ctl 2.2.2 epoll_wait 2.2.3 epoll_create 三、 epoll 类的编写 3.1 类的框…

大数据-64 Kafka 高级特性 分区 分区重新分配 实测

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…

使用uwsgi部署Flask应用

前言&#xff1a;本人大四&#xff0c;研0&#xff0c;在24年暑假在杭州某互联网公司实习&#xff0c;本文用来记录自己在公司学到的东西。 uwsgi&#xff1a;uWSGI是一个Web服务器&#xff0c;它实现了WSGI协议、uwsgi、http等协议。Nginx中HttpUwsgiModule的作用是与uWSGI服…

集合的基本使用

数据和集合的区别 数组可以存储基本数据类型和引用数据类型。 但是&#xff0c;集合不可以直接存储基本数据类型&#xff0c;需要以包装类的方式进行存储&#xff0c;其可以存储引用数据类型。 ArrayList 成员方法 import java.util.ArrayList;/*** ClassName Test* author …

[Meachines] [Easy] Postman redis未授权访问-SSH公钥注入+RSA私钥解密+Webmin-RCE权限提升

信息收集 IP AddressOpening Ports10.10.10.160TCP:22,80,6379,10000 $ nmap -p- 10.10.10.160 --min-rate 1000 -sC -sV PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0) | ssh-hostkey: | 2048…

计算机基础(Windows 10+Office 2016)教程 —— 第7章 演示文稿软件PowerPoint 2016

第7章 演示文稿软件PowerPoint 2016 7.1 PowerPoint 2016入门7.1.1 PowerPoint 2016 简介7.1.2 PowerPoint 2016 的操作界面组成7.1.3 PowerPoint 2016 的窗口视图方式7.1.4 PowerPoint 2016 的演示文稿及其操作7.1.5 PowerPoint 2016 的幻灯片及其操作 7.2 演示文稿的编辑与设…

win 10 局域网共享

1&#xff0c;打开共享 控制面板\网络和 Internet\网络和共享中心\高级共享设置 &#xff08;在控制面板界面建议使用大图片或小图标容易找到目标&#xff09; 或者直接复制红色部分&#xff0c;然后打开此电脑&#xff0c;粘贴到地址栏直接回车即可直接到达几面 打开如下2个…

HCIP-综合实验

实验题目与要求如下所示&#xff1a; 根据题目要求进行划分网段&#xff1a; 1、配置各个路由器的ip地址以及环回地址&#xff1a; R1 R2 R3 R4 R5 R6 R7 2、配置rip的基本功能&#xff1a;启动rip进程&#xff0c;指定网段使用rip R1 R2 R3 R4 R5 R6 R7 3、R1创建环回172.…

微信答题小程序产品研发-页面交互设计

答题小程序页面交互设计&#xff0c;针对答题小程序的所有主要页面和功能模块&#xff0c;包括首页、轮播图、公告、微信授权登录、题库练习、出题考试、错题集、答题历史、收藏、个人中心等。 1、目的 答题小程序页面为什么要进行交互设计&#xff1f; 据我分析&#xff0c;…

品牌热度维系策略:深度触达,让每一次互动都成为爱的记忆累积

在浩瀚的商业宇宙中&#xff0c;每一个品牌都如同夜空中闪烁的星辰&#xff0c;它们或明或暗&#xff0c;共同编织着市场的繁星图景。当谈及品牌与消费者之间那份微妙而深邃的情感联结时&#xff0c;我们不禁要深入探讨一个核心议题&#xff1a;在快速迭代的消费时代&#xff0…

二十天刷leetcode【hot100】算法- day1[后端golang]

哈希表 其他语言版本 ts-day1 js-day1 python-day1 1. 两数之和 给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target 的那 两个 整数&#xff0c;并返回它们的数组下标。 你可以假设每种输入只会对应一个答案。但是&#xf…

ue5 打开关卡,未正常加载地形及物品。资产必须放在content目录下

1.资产必须放在content目录下 2.要把A拿到B去用&#xff0c;必须使用资产迁移&#xff0c;不可直接拷贝&#xff0c;因为有依赖文件

Qt 学习第四天:信号和槽机制(核心特征)

信号和槽的简介 信号和插槽用于对象之间的通信。信号和插槽机制是Qt的核心特征&#xff0c;可能是不同的部分大部分来自其他框架提供的特性。信号和槽是由Qt的元对象系统实现的。介绍&#xff08;来自Qt帮助文档Signals & Slots&#xff09; 在GUI编程中&#xff0c;当我们…

安装sqllab靶机之后,练习关卡报403 forbidden

解决办法&#xff1a; 在nginx的conf文件中添加上访问index.php vim /usr/local/nginx/conf/nginx.conf 保存退出 再重启一下nginx&#xff0c;就完成了。 ./nginx -s reload

中国AI大模型场景探索及产业应用调研报告

AI大模型发展态势 定义 AI大模型是指在机器学习和深度学习领域中&#xff0c;采用大规模参数(至少在一亿个以上)的神经网络模型&#xff0c;AI大模型在训练过程中需要使用大量的算力和高质量的数据资源。 产业规模 2023年&#xff0c;中国大模型市场规模为147亿。结合《202…