Linux 编写一个 简单进度条

news2025/2/1 8:02:24

进度条

回车换行理解:

我们要理解,回车换行是两个概念:

  • 换行是把光标移到下一行,是竖直的往下平移;" \n "
  • 回车是把光标移到当前行的最开始;                  " \r " 

 就和一起打字机一样:

 在打字的时候,有一个 负责打印印刷在纸上的机器,是跟随者使用人打印文字,往有迭代走的,当这个机器走到 最右边的时候,或者使用人想要再次换行的时候,使用人机会把这个机器从最右边 手动 移动到 最左边,这个过程就和上述的 "r " 换行一样。

而且在换行的时候,纸还会往上走,这个过程就相当于是 回车了。

还有老式键盘的回车键,是 一个 从上往下,从左往右的造型:

 这也是模拟了 此处的回车键 是代表的是 回车换行的作用。

 缓冲区理解

 首先我们来看一个简单的代码:

 我们在 Linux 下运行这个代码的话,会先打印 hello C! 然后 程序休眠两秒 才会出来下一个 word:

 但是这是我们在printf 当中输出了 \n 的情况;如果不输出  \n 的话 又会是什么情况呢?

程序源码展示:

 输出:

 发现好像不对劲啊,C语言一定是从 上往下执行的,那么应该是 先输出 hello C! 然后在 休眠啊。

但是看上述的输出结果,好像是 休眠在输出啊?

所以,上述printf()已经打印完毕了,只不过还没有显示出来,那么在 sleep()的两秒内,这个 "hello C!" 字符串是在哪里存储的呢?

其实就是存储在 缓冲区当中的,因为 上述使用 C语言编写代码,所以此时,这个缓冲区是由 C语言管理的一块缓冲区。

当程序 sleep 结束了在缓冲区当中的数据才会被刷出来。

C程序默然会为我们打开三个 输入输出流:标准输入,标准输出(显示器,输出类型为 stdout),标准错误。

stdout 其实就是 FILE* 类型;

 我们可以使用 stdio.h 当中的 fflush(FILE* stream)函数,强制把 缓冲区当中的数据给刷出来。

 输出:

 至此,我们就可以简单的实现一个倒计时了:

 上述的 "-" 表示每一次都从 最左边开头输出;"2" 表示,不管当前输出什么,都按照两个字符输出;"\r" 表示每一次都当前行的最左边输出。

如果不加 "\r" 将会是 一秒输出一个数字 输出: 109876543210 。

进度条实现

那么在linux 当中,实现一个简单的进度条就和上述倒计时小程序是类似的,我们只需要把 每一次 光标移动到最前面的时候输出的字符,从 1 个,改为依次增加一个字符输出就行了。

利用就是 linux 当中不使用 "/n" 等等类似操作的话,就不会立即刷新缓冲区当中的内容,我们把这种刷新方式称之为 行刷新。比如:使用 "\r" 回车的话,就不会立即刷新 缓冲区当中的内容,也就不会立即打印出来。

我们可以使用 fflush(stdout)的方式来强制输出 缓冲区当中的内容。

我们先把 中间填充的部分完善,按照上述倒计时的方式修改。

 上述printf()函数当中的输出,“100” 是为了保证 在 “[]”当中一直有最大 100 个字符的 “\0” 填充,"-" 代表从当前行最左边开始输出(因为 当使用上述 的填充之后,C语言默认从最最右边开始输出), "%%" 是输出一个 "%",因为 "%" 这个字符在printf ()当中是格式化输出的字符,所以只能直接用,当然 可以 "/%" 使用转移字符。

上述输出:

 但是,上述的进度条有点太单一了,我们还可以加上一些旋转图标等等,我们就简单实现,覆盖式打印 "|"  "/"  "--" "\"  "|"  ,简单实现。

完整代码:

  1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<string.h>
  4 
  5 #define NUM 100
  6 #define STYLE '='
  7 #define RIGHT '>'
  8 #define TOP 100
  9 #define SPEED 10000
 10 
 11 const char* labar = "|/-\\";
 12 
 13 void processbar(int speed)
 14 {
 15   int cn = 0;
 16   char array[NUM];
 17 
 18   memset(array, '\0', sizeof(array));
 19   int len = strlen(labar);
 20 
 21   while(cn<TOP)
 22   {
 23     printf("[%-100s][%d%%][%c]\r",array, cn + 1 ,labar[cn%len]);
 24     fflush(stdout);
 25     array[cn++] = STYLE;
 26 
 27     if(cn < TOP) array[cn] = RIGHT;                                                                                 
 28     usleep(SPEED);
 29   }
 30   printf("\n");
 31 }
 32 
 33 int main()
 34 {
 35   processbar(10000);
 36   
 37   return 0;
 38 }

上述是进度条自己在运行,但是我们在使用进度条的时候,一般都是记录一下外部程序运行的进度的,那么进度条函数本身是不知道的,只有外部程序调用者才知道当前 程序的进度,所以我们还需要对上述进度条进行改进:

#include<stdio.h>
#include<unistd.h>
#include<string.h>

#define NUM 100
#define STYLE '='
#define RIGHT '>'
#define TOP 100

const char* labar = "|/-\\";
char array[NUM] = { 0 };

void processbar(int rate)
{
	if (rate < 0 || rate > 100) return;

	17   int len = strlen(labar);
	printf("[%-100s][%d%%][%c]\r", array, rate, labar[rate % len]);
	fflush(stdout);
	array[rate++] = STYLE;

	if (rate < 100) array[rate] = RIGHT;
}

int main()
{
	int total = 1000; // 比如现在要下载一个 1000MB的文件
	int curr = 0;

	//因为进度条是在外部使用进度条的人才知道当前程序的进度
//所以 循环不能写在 进度条函数当中
//应该写在外部
	while (curr <= total)
	{
		//这里就模拟外部程序运行了

		processbar(curr * 100 / total);
		curr += 10;
		usleep(50000);
	}

	printf("\n");

	return 0;
}

此时,在进度条函数当中,就只有 100 打印当中的一次答打印,我们把 循环做到了外层 程序当中。

其实还可以更简化,可以使用 函数指针类型 来实现更简单的进度条调用

typedef void (*callback_t)(int); // 函数指针类型

 // 模拟一种下载
void download(callback_t cb)
{
	int total = 1000;
	int curr = 0;
	while (curr <= total)
	{
		// 此时正在下载
		usleep(50000); // 模拟下载时间
		int rate = curr * 100 / total;

		cb(rate);// 通过函数指针的回调,调用 processbar函数

		curr += 10;
	}
	printf("\n");
}
void processbar(int rate)
{
	if (rate < 0 || rate > 100) return;

	int len = strlen(labar);
	printf("[%-100s][%d%%][%c]\r", array, rate, labar[rate % len]);
	fflush(stdout);
	array[rate++] = STYLE;

	if (rate < 100)
	{
		array[rate] = RIGHT;
	}
	else
	{
		initarray();
	}
}

 

如上所示,我们模拟实现一个 下载软件的 过程,把 进度条函数通过函数指针的方式传入到 download当中,在 download()函数当中,通过函数指针调用到 进度条函数,就可以简便的调用进度条函数。

 所以,在主函数当中,我们只需要调用 download()函数,就行:

int main()
{
	download(processbar);

	return 0;
}

 上述就是 简单的进度条实现,当然,你还可以用 C当中一些颜色输出来美化 进度条:
c语言中如何更改输出字符的颜色 - CSDN文库

 

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

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

相关文章

[计算机提升] Windows系统权限

1.2 Windows系统权限 在Windows操作系统中&#xff0c;权限是指授予用户或用户组对系统资源进行操作的权利。权限控制是操作系统中重要的安全机制&#xff0c;通过权限控制可以限制用户对系统资源的访问和操作&#xff0c;从而保护系统安全。 Windows操作系统中包含以下几种权…

FPGA设计时序约束五、设置时钟不分析路径

一、背景 在进行时序分析时&#xff0c;工具默认对所有的时序路径进行分析&#xff0c;在实际的设计中&#xff0c;存在一些路径不属于逻辑功能的&#xff0c;或者不需要进行时序分析的路径&#xff0c;使用set_false_path对该路径进行约束&#xff0c;时序分析时工具将会直接忽…

通过onnxruntime进行模型部署过程中的问题

1.onnxruntime包 从https://github.com/microsoft/onnxruntime/releases/下载解压到D:/code/package/onnruntime 2opencv_c下载https://github.com/opencv/opencv/releases/tag/4.8.1 3.测试opencv代码&#xff1a;总结&#xff1a;添加include目录&#xff0c;添加lib库目录…

网工配置命令总结(1)---Web访问及vlan配置

目录 1.Telnet远程登录设备 2.用户通过Web网管登录设备 3.端口隔离功能的实现 4.配置vlan聚合 5.部分vlan间互通&#xff0c;部分vlan间隔离&#xff0c;vlan内用户隔离 6.限制内网网段互访 1.Telnet远程登录设备 1.使能服务器功能 <HUAWEI> system-view [HUAWEI] …

Eslint配置 Must use import to load ES Module(已解决)

最近在配置前端项目时&#xff0c;eslint经常会碰到各种报错&#xff08;灰常头疼~&#xff09; Syntax Error Error No ESLint configuration found.Syntax Error: Error: D:\dmq\dmq-ui.eslintrc.js: Environment key “es2021” is unknown at Array.forEach ()error in ./…

GO 语言处理并发的时候我们是选择sync还是channel

以前写 C 的时候&#xff0c;我们一般是都通过共享内存来通信&#xff0c;对于并发去操作某一块数据时&#xff0c;为了保证数据安全&#xff0c;控制线程间同步&#xff0c;我们们会去使用互斥锁&#xff0c;加锁解锁来进行处理 然而 GO 语言中建议的时候通过通信来共享内存&…

看图学习数据中心机柜导轨方面的英文术语

对于一些数据中心的服务器&#xff0c;存储&#xff0c;交换机等设备的上架安装&#xff0c;有些导轨设计还是有点复杂的&#xff0c;如果安装手册还是英文的&#xff0c;就更有些挑战了。本文用一个实际的导轨图片来对其中常用的一些英文术语做了一个详细解释&#xff0c;供朋…

面试官:Go GMP 模型为什么 P 组件如此重要 ?

大家好&#xff0c;我是木川 Go GMP模型 是 Go语言并发性能的关键组成部分&#xff0c;它允许轻松创建大量的 Goroutines&#xff0c;设计思想包括并行性、线程复用以及抢占调度。 Go 1.1 版本前采用的是 GM 模型&#xff0c;存在一些问题&#xff0c;后面增加了 P 组件&#x…

哪种烧录单片机的方法合适?

哪种烧录单片机的方法合适&#xff1f; 首先&#xff0c;让我们来探讨一下单片机烧录的方式。虽然单片机烧录程序的具体方法会因为单片机型号、然后很多小伙伴私我想要嵌入式资料&#xff0c;通宵总结整理后&#xff0c;我十年的经验和入门到高级的学习资料&#xff0c;只需一…

计算机算法分析与设计(9)---0-1背包和完全背包问题(含C++代码)

文章目录 一、0-1背包概述1.1 问题描述1.2 算法思想 二、0-1背包代码2.1 题目描述2.2 代码编写 三、完全背包概述四、完全背包代码4.1 题目描述4.1 代码编写4.2 代码优化 一、0-1背包概述 1.1 问题描述 1. 0-1背包问题&#xff1a;给定 n n n 种物品和一背包。物品 i i i 的…

Unity可视化Shader工具ASE介绍——7、ASE实现Matcap效果和自定义节点

大家好&#xff0c;我是阿赵。继续介绍Unity可视化Shader编辑工具ASE。上一篇用了很长的篇幅来做了一个遮挡X光的效果。这一篇来做一个MatCap效果。不过做MatCap并不是目的&#xff0c;是想说明一下&#xff0c;怎样在ASE里面自定义方法节点。 一、在ASE里面做MatCap材质 由于…

【每日一题Day348】LC137只出现一次的数字Ⅱ | 状态转移

只出现一次的数字Ⅱ【LC137】 给你一个整数数组 nums &#xff0c;除某个元素仅出现 一次 外&#xff0c;其余每个元素都恰出现 **三次 。**请你找出并返回那个只出现了一次的元素。 你必须设计并实现线性时间复杂度的算法且不使用额外空间来解决此问题。 排序 将数组从小到大…

LED灯的基本控制

题目要求 首先让8路LED指示灯闪烁3遍然后熄灭&#xff0c;接着依次点亮LED指示灯&#xff0c;最后依次熄灭LED灯&#xff0c;程序循环实现上述功能。 分析设计 原理图&#xff1a; 首先应该选通Y4C&#xff0c;也就是ABC 100。 代码 #include <regx52.h> #define…

C语言 —— 指针

目录 1. 指针是什么&#xff1f; 2. 指针和指针类型的关系 2.1 指针的解引用 2.2 指针-整数 3. 野指针 3.1 野指针成因 1. 指针未初始化 2. 指针越界访问 3. 指针指向的空间释放 3.2 如何规避野指针 4. 指针运算 4.1 指针-整数 4.2 指针-指针 指针-指针的使用 4.3 指针的关系运…

3.7 static关键字

思维导图&#xff1a; 3.7.1 静态属性 ### 3.7 static关键字 --- Java提供了一个称为static的关键字&#xff0c;用于修饰类的成员&#xff0c;如成员变量、成员方法和代码块。使用static修饰的成员拥有特殊性。 --- #### 3.7.1 静态属性 当属性被static关键字修饰时&…

嵌入式实时操作系统的设计与开发(轮询系统学习)

轮询系统具有以下工作特点&#xff1a;系统完成一个轮询的时间取决于循环中需要执行的函数个数。此外&#xff0c;轮询的次序是静态固定的&#xff0c;在运行时不能进行动态调整。 典型系统 许多工业线程网络中&#xff0c;由于需要控制的设备较多、相互距离又较远&#xff0…

手把手教你用Python绘制神经网络图

接下来教大家如何使用 Python 中的 networkx 库&#xff0c;绘制美观且标准的神经网络。会根据指定的层和节点数量&#xff0c;绘制不同结构的神经网络。 networkx 库可以用来创建和操作图类型的数据结构&#xff0c;其中包括无向图、有向图、带权图等等。 神经网络可以看做是一…

字节码之 Lambda 表达式底层原理

文章目录 0.前言0. lambda程序示例1. 编译程序&#xff1a;2. 使用 javap 分析字节码3. 输出字节码4. 分析指令 1. Lambda 表达式的字节码实现1.1 什么是invokedynamic 指令invokedynamic 的工作原理为何 invokedynamic 如此特殊&#xff1f; 1.2 bootstrap method 详解1.1 Lam…

Qt之给控件添加右键菜单

一、设置控件 在对应控件的属性中&#xff0c;将contextMenuPolicy设置为CustomContextMenu。 二、添加槽函数 在对应控件上右键选择槽函数customContextMenuRequested(QPoint)。 三、在槽函数中添加右键菜单 在槽函数中输入如下代码&#xff0c;添加右键菜单。 //右键菜单 …

红帽Linux的安装和部署

目录 一、红帽Linux的安装阶段 1、下载redhat7.9的iso镜像 2、安装阶段 二、红帽Linux的配置阶段 1、第一次进入装机配置 2、进入机器后的一些配置 三、远程连接阶段 1、关闭防火墙 2、使用Xshell远程连接&#xff08;其他连接工具也行&#xff09; 1.开启SSH服务 2.连…