Linux第一个系统程序---进度条

进度条---命令行版本

回车换行

其实本质上回车和换行是不同概念,我们用一张图来简单的理解一下:

在计算机语言当中:

换行符:\n

回车符:\r

\r\n:回车换行

这时候有人可能会有疑问:我在学习C/C++语言的时候,单纯的\n就起到了回车加换行的行为呀?!

答:因为在语言层面\n给我们编译成了\r\n 

缓冲区问题

在我们目前的阶段,我们将缓冲区看作是一段内存块,我们可以先实现一个测试代码:

#touch test.c#可以直接
vim test.c

测试代码

技巧补充:我们可以在底行模式使用man手册查看sleep(3)函数所需的头文件:
:!man 3 sleep

#include<stdio.h>
#include<unistd.h>int main()
{
//printf("hello Linux!\n");printf("hello Linux!");sleep(3);return 0;
}

 我们在Linux编译后./test执行时,当只有上被注释的代码被执行,和只有第二句printf的代码,我们发现前者内容打印出来,程序3秒后结束,后者是3秒后程序结束才打印内容,我们可以明显感受到程序是先执行sleep(3)的,但是我们之前学习C/C++知道,我们定义的程序逻辑都是从上往下执行的,可是我们为什么没有看到字符串呢?

我们应该知道,对于后者,printf在sleep之前一定执行完了,但是显示器上没有显示,那么在我们休眠3秒期间,字符串" hello Linux! "在哪里?其实该字符串在缓冲区里面,可以理解为内存有一小内存块,将该字符串临时保存在该处了。我们带有\n的字符串可以在显示屏上打印,我们称为行刷新,也就是遇到\n,printf执行完就默认将其字符串直接显示到显示器了(行刷新),可以理解为没有\n就不给行刷新;

那后者为什么最后也可以刷新出来呢?

是因为程序退出了,会自动刷新缓冲区!


我们如何可以让不带\n的字符串能够立马刷新呢?

我们如果想让不带\n的字符串立马刷新,可以使用fflush:

fflush 是 C 语言标准库函数之一,用于刷新流的缓冲区。其原型定义在 <stdio.h> 头文件中

我们可以进行man手册查看。

printf("hello Linux!");
fflush(stdout);

其实我们的程序会默认打开三个输入输出流,分别是:(描述符分别为1,2,3)

  1. 标准输入流(stdin:用于从标准输入设备(通常是键盘)读取数据。

  2. 标准输出流(stdout:用于向标准输出设备(通常是屏幕)输出数据。

  3. 标准错误流(stderr:用于输出错误信息和其他诊断信息,通常也是输出到屏幕。

都是一个指向 FILE 结构体的指针,定义在 <stdio.h> 中。

我们的printf默认是从标准输出流里面打,我们可以看到fprintf其实就是比printf多了一个参数:

在Linux下,一切皆文件,当一个程序在 Linux 下运行时,操作系统会自动为该程序打开三个标准流文件描述符,也就是下面两条代码是等价的,fprintf只是显示的表示出往哪里输出:

    fprintf(stdout,"hello Linux!\n");printf("hello Linux!\n");

 我们就可以利用缓冲区来实现一个倒计时:光标显示后回退(回车)

int main()
{int i=9;while(i>=0){printf("%d\r",i);i--;sleep(1);}return 0;
}

但是我们发现在显示器上没有显示任何数据,这是为什么呢?

因为我们行刷新是需要\n的,%d后是\r,\r不支持我们行刷新,所以对应的信息并未有显示出来,还在缓冲区里面存着呢,所以需要用到fflush(stdout);

int main()
{int i=9;while(i>=0){printf("%d\r",i);fflush(stdout);i--;sleep(1);}printf("\n");//跑完之后想要保留0(命令行),不让会覆盖return 0;
}

为了使我们对下面进度条程序更好的实现,我们来看看当i取10的时候:

命令行变化:10-90-80-70-60-50-40-30-20-10-00----00

这时候,我们应该好好理解显示:
举个例子,我们往显示器上输入:12345,是输出的是12345数字,还是'1' '2' '3' '4' '5'字符?

显示器只认识字符!显示器是字符设备,所以输出的是后者,这也是为什么我们需要像%d的格式化输出,所以我们可以将上述代码中的%d改成%2d,还可以在前面加一个-即"%-2d"来整体靠左显示,代码我就不写出来了,在原代码上做改进就🆗。

以上我们进度条的预备工作就基本完成了(回车换行,缓冲区问题,格式问题,字符设备的理解问题,输出设备的刷新问题)

我们想写一个怎么样的进度条

框架的搭建

创建文件

首先,在我们自己的工作目录中创建四个文件:

  • main.c

  • Makefile

  • process.c

  • process.h

编写 Makefile

使用 vim 编辑器打开 Makefile,并写入以下内容:

SRC:=$(wildcard *.c)
OBJ:=$(SRC:.c=.o)
BIN=processbar$(BIN): $(OBJ)gcc -o $@ $^%.o: %.cgcc -c $<.PHONY: clean
clean:rm -f $(OBJ) $(BIN)

这个 Makefile 定义了如何编译和链接你的程序。它使用 wildcard 自动查找所有 .c 文件,并生成相应的 .o 文件。最终生成的可执行文件名为 processbar

编写 process.h

使用 vim 编辑器打开 process.h,并写入以下内容:

#pragma once
#include <stdio.h>void process_v1();

这个头文件声明了一个函数 process_v1,该函数将在 process.c 中实现。

编写 process.c

使用 vim 编辑器打开 process.c,并写入以下内容:

#include "process.h"void process_v1() {printf("hello rose!\n");
}

这个文件实现了 process_v1 函数,目前只是简单地打印一条消息。

编写 main.c

使用 vim 编辑器打开 main.c,并写入以下内容:

#include "process.h"int main() {process_v1();return 0;
}

这个文件是程序的入口点,它调用了 process_v1 函数。

编译和运行

完成上述文件编写后,你可以使用以下命令进行编译:

make

这将根据 Makefile 的规则编译并生成 processbar 可执行文件。然后,你可以运行该程序:

./processbar

如果一切顺利,你应该会看到输出 "hello rose!"。

清理

最后,使用以下命令清理生成的文件:

make clean

这将删除所有 .o 文件和 processbar 可执行文件。

接下来,我们就可以在 process.c 中实现进度条的功能。可以根据具体需求设计进度条的更新逻辑和显示方式。

version1原理版本

#include"process.h"
#include<string.h>
#include<unistd.h>#define NUM 101//因为会有一个\0的存在
#define STYLE '='void process_v1()
{    char buffer[NUM];memset(buffer,0,sizeof(buffer));const char *lable="|/-\\";size_t len=strlen(lable);//计数器int cnt=0;while(cnt<=100)//这个循环会循环101次{printf("[%-100s][%d%%][%c]\r",buffer,cnt,lable[cnt%len]);fflush(stdout);buffer[cnt]=STYLE;cnt++;//sleep(1);usleep(10000);}printf("\n");return;
}
  1. 定义常量和变量

    • NUM 定义为 101,因为进度条的长度为 100 个字符,加上一个字符串的终止符 \0
    • STYLE 定义为 =,用于表示进度条的已填充部分。
    • buffer 是一个字符数组,用于存储进度条的当前状态。
    • lable 是一个字符串,包含旋转的符号 |/-\,用于在进度条旁边显示一个旋转的动画效果。
    • len 是 lable 字符串的长度。
    • cnt 是一个计数器,用于控制进度条的更新和旋转符号的切换。
  2. 初始化缓冲区

    • 使用 memset 函数将 buffer 初始化为全 0,即空字符串。
  3. 进度条更新循环

    • while 循环会执行 101 次,因为 cnt 从 0 开始,直到 100 结束。
    • 在每次循环中,使用 printf 函数输出当前的进度条状态:
      • [%-100s] 表示一个宽度为 100 的左对齐字符串,buffer 作为参数传入,表示进度条的当前填充状态。
      • [%d%%] 表示当前的百分比进度,cnt 作为参数传入。
      • [%c] 表示旋转符号,lable[cnt%len] 计算当前应该显示的旋转符号。
    • 使用 \r 作为 printf 的结尾,表示返回到行首,这样下一次输出会覆盖当前行的内容。
    • 调用 fflush(stdout) 确保输出立即显示在屏幕上,而不是被缓冲。
    • 将 buffer[cnt] 设置为 STYLE,即 =,表示进度条的已填充部分向右扩展一个字符。
    • cnt++ 更新计数器。
    • usleep(10000) 暂停 10 毫秒,使进度条的更新速度适中,便于观察。注释掉的 sleep(1) 是暂停 1 秒的另一种方式,但会使进度条更新过慢。
  4. 结束输出

    • 循环结束后,调用 printf("\n") 输出一个换行符,使光标移动到下一行,避免后续输出覆盖进度条。

整体功能:

这个 process_v1 函数实现了一个动态更新的文本进度条,进度条的长度为 100 个字符,旁边有一个旋转的符号动画。通过在循环中逐步填充 buffer 并输出,模拟了进度条的动态增长过程。每次更新后,使用 \r 返回行首并刷新输出,实现了进度条的原地更新效果。

version2真实版本

main.c

#include "process.h"#include <stdio.h>
#include <unistd.h>double total = 1024.0; // 总下载量,单位为MB
double speed = 1.0;    // 下载速度,单位为MB/svoid DownLoad() {double current = 0; // 当前已下载量while (current <= total) {// 刷新进度条FlushProcess(total, current);// 模拟下载数据,每次循环下载speed大小的数据usleep(300000); // 暂停300ms,模拟下载延时current += speed;}printf("\ndownload %.2lfMB Done\n", current);
}int main() {DownLoad();return 0;
}

version2

#include "process.h"
#include <string.h>
#include <unistd.h>#define NUM 101 // 因为会有一个\0的存在
#define STYLE '='// version2
void FlushProcess(double total, double current) {char buffer[NUM];memset(buffer, 0, sizeof(buffer));const char *lable = "|/-\\";size_t len = strlen(lable);static int cnt = 0; // 静态变量,用于记录旋转符号的位置// 计算当前进度条的填充数量int num = (int)(current * 100 / total);for (int i = 0; i < num; i++) {buffer[i] = STYLE;}double rate = current / total; // 计算当前下载进度的百分比cnt %= len;                    // 计算旋转符号的索引printf("[%-100s][%.1f%%][%c]\r", buffer, rate * 100, lable[cnt]);cnt++;fflush(stdout); // 确保进度条立即显示
}

进度条与下载模拟功能梳理:

  • 下载模拟

    • DownLoad 函数模拟了一个下载过程,其中 total 表示总下载量,speed 表示下载速度。
    • 在 while 循环中,每次循环模拟下载 speed 大小的数据,并通过 usleep 函数暂停一段时间来模拟下载延时。
    • 循环过程中,不断调用 FlushProcess 函数来刷新进度条,显示当前的下载进度。
  • 进度条刷新

    • FlushProcess 函数负责根据当前下载量 current 和总下载量 total 计算进度条的填充状态,并输出到屏幕。
    • buffer 数组用于存储进度条的当前状态,num 变量计算出需要填充的 = 字符的数量。
    • 使用 printf 函数输出进度条,其中 [%-100s] 表示一个宽度为 100 的左对齐字符串,用于显示进度条的填充部分;[%.1f%%] 显示当前的下载百分比;[%c] 显示一个旋转的符号,用于增加动画效果。
    • 使用 \r 作为 printf 的结尾,表示返回到行首,使下一次输出覆盖当前行的内容,实现进度条的原地更新效果。
    • fflush(stdout) 确保进度条的输出立即显示在屏幕上,而不是被缓冲。

整体功能

这个程序通过模拟下载过程,并在每次下载数据后刷新进度条,实现了动态显示下载进度的效果。进度条的长度为 100 个字符,旁边有一个旋转的符号动画,使用户可以直观地看到下载进度的变化。当下载完成后,程序会输出一条完成信息,告知用户下载已成功完成。

我们不仅仅可以在下载方面使用进度条,我们也可以在上传方面使用进度条,因此,在main.c源文件当中,我们typedef一个函数指针来使用回调函数来优化代码:

#include"process.h"#include<stdio.h>
#include<unistd.h>typedef void (*callback_t) (double total,double current);//函数指针double total=1024.0;
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);
}void UpLoad(callback_t cb)
{double current=0;while(current<=total){//刷新进度cb(total,current);//下载代码usleep(3000);//充当下载数据current+=speed;}printf("\nUpload %.2lfMB Done\n",current);
}
int main()
{DownLoad(FlushProcess);UpLoad(FlushProcess);return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JavaSE——网络编程

一、InetAddress类 InetAddress是Java中用于封装IP地址的类。 获取本机的InetAddress对象&#xff1a; InetAddress localHost InetAddress.getLocalHost();根据指定的主机名获取InetAddress对象&#xff08;比如说域名&#xff09; InetAddress host InetAddress.getByNa…

CompletableFuture // todo

相比较所有代码都在主线程执行&#xff0c;使用Future的好处&#xff1a;利用服务器多核、并发的优势。 不足&#xff1a; 开启没有返回值的异步线程&#xff1a; 1、runAsync 使用lambda表达式&#xff1a; 开启有返回值的异步线程&#xff1a; 1、supplyAsync 异步任务中的…

RabbitMQ基本介绍及简单上手

&#xff08;一&#xff09;什么是MQ MQ&#xff08;message queue&#xff09;本质上是队列&#xff0c;满足先入先出&#xff0c;只不过队列中存放的内容是消息而已&#xff0c;那什么是消息呢&#xff1f; 消息可以是字符串&#xff0c;json也可以是一些复杂对象 我们应用场…

跟着逻辑先生学习FPGA-第六课 无源蜂鸣器发声实验

硬件平台&#xff1a;征战Pro开发板 软件平台&#xff1a;Vivado2018.3 仿真软件&#xff1a;Modelsim10.6d 文本编译器&#xff1a;Notepad 征战Pro开发板资料 链接:https://pan.baidu.com/s/1AIcnaGBpNLgFT8GG1yC-cA?pwdx3u8 提取码:x3u8 1 知识背景 1.1 蜂鸣器简介 蜂鸣…

TensorFlow Quantum快速编程(高级篇)

五、实战:量子分类器应用 5.1 数据准备 在实战构建量子分类器时,数据准备是基石环节。选用鸢尾花数据集,这一经典数据集在机器学习领域应用广泛,其涵盖了三种鸢尾花品种的样本,每个样本包含花萼长度、花萼宽度、花瓣长度、花瓣宽度四个特征。鉴于本次构建二分类量子分类…

Mysql进阶篇

一&#xff1a;存储引擎 二&#xff1a;索引 2.1 索引概述 索引&#xff08;index&#xff09;帮助mysql高效获取数据的数据结构&#xff08;有序&#xff09;。在数据之外&#xff0c;数据库系统还维护着满足特定查找算法的数据结构&#xff0c;这些数据结构以某种方式引用&…

通过gradle发布aar或jar携带sources-jar到maven nexus

找了很久&#xff0c;没有找到满意的。终于找到一个好的办法。 gradle7.x适用。比以前的写法简洁。 发布传统的jar工程 比如okhttp&#xff0c;fastjson等项目&#xff0c;纯java工程。 直接创建新文件publish.gradle: apply plugin: maven-publishProperties properties …

ELK日志分析实战宝典之ElasticSearch从入门到服务器部署与应用

目录 ELK工作原理展示图 一、ElasticSearch介绍&#xff08;数据搜索和分析&#xff09; 1.1、特点 1.2、数据组织方式 1.3、特点和优势 1.3.1、分布式架构 1.3.2、强大的搜索功能 1.3.3、数据处理与分析 1.3.4、多数据类型支持 1.3.5、易用性与生态系统 1.3.6、高性…

2025年VGC大众汽车科技社招入职测评综合能力英语口语SHL历年真题汇总、考情分析

早在1978年&#xff0c;大众汽车集团就开始了与中国的联系。1984年&#xff0c;集团在华的第一家合资企业—上汽大众汽车有限公司奠基成立&#xff1b;1991年&#xff0c;一汽-大众汽车有限公司成立&#xff1b;2017年&#xff0c;大众汽车&#xff08;安徽&#xff09;有限公司…

嵌入式C语言:二维数组

目录 一、二维数组的定义 二、内存布局 2.1. 内存布局特点 2.2. 内存布局示例 2.2.1. 数组元素地址 2.2.2. 内存布局图&#xff08;简化表示&#xff09; 2.3. 初始化对内存布局的影响 三、访问二维数组元素 3.1. 常规下标访问方式 3.2. 通过指针访问 3.2.1. 指向数…

H2数据库在单元测试中的应用

H2数据库特征 用比较简洁的话来介绍h2数据库&#xff0c;就是一款轻量级的内存数据库&#xff0c;支持标准的SQL语法和JDBC API&#xff0c;工业领域中&#xff0c;一般会使用h2来进行单元测试。 这里贴一下h2数据库的主要特征 Very fast database engineOpen sourceWritten…

R语言在森林生态研究中的魔法:结构、功能与稳定性分析——发现数据背后的生态故事!

森林生态系统结构、功能与稳定性分析与可视化研究具有多方面的重要意义&#xff0c;具体如下&#xff1a; 一、理论意义 ●深化生态学理论 通过研究森林生态系统的结构、功能与稳定性&#xff0c;可以深化对生态系统基本理论的理解。例如&#xff0c;生物多样性与生态系统稳定性…

【C++经典例题】求1+2+3+...+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句

&#x1f493; 博客主页&#xff1a;倔强的石头的CSDN主页 &#x1f4dd;Gitee主页&#xff1a;倔强的石头的gitee主页 ⏩ 文章专栏&#xff1a; 期待您的关注 题目描述&#xff1a; 原题链接&#xff1a; 求123...n_牛客题霸_牛客网 (nowcoder.com) 解题思路&#xff1a; …

day01-HTML-CSS——基础标签样式表格标签表单标签

目录 此篇为简写笔记下端1-3为之前笔记&#xff08;强迫症、保证文章连续性&#xff09;完整版笔记代码模仿新浪新闻首页完成审核不通过发不出去HTMLCSS1 HTML1.1 介绍1.1.1 WebStrom中基本配置 1.2 快速入门1.3 基础标签1.3.1 标题标签1.3.2 hr标签1.3.3 字体标签1.3.4 换行标…

基于Springboot+Vue的仓库管理系统

开发一个基于Spring Boot和Vue的仓库管理系统涉及到前端和后端的开发。本文呢&#xff0c;给出一个简单的开发步骤指南&#xff0c;用于指导初入的新手小白如何开始构建这样一个系统&#xff0c;如果**你想直接学习全部内容&#xff0c;可以直接拉到文末哦。** 开始之前呢给小…

快速导入请求到postman

1.确定请求&#xff0c;右键复制为cURL(bash) 2.postman菜单栏Import-Raw text&#xff0c;粘贴复制的内容保存&#xff0c;请求添加成功

预训练语言模型——BERT

1.预训练思想 有了预训练就相当于模型在培养大学生做任务&#xff0c;不然模型初始化再做任务就像培养小学生 当前数据层面的瓶颈是能用于预训练的语料快被用完了 现在有一个重要方向是让机器自己来生成数据并做微调 1.1 预训练&#xff08;Pre - training&#xff09;vs. 传…

数字孪生电网有什么作用?实时云渲染技术又如何赋能智慧电网?

电网系统的结构比较复杂&#xff0c;传统运维模式主要是依赖传感器和人工巡检&#xff0c;难以全面监测管理。而数字孪生技术的应用将推动电网智能化、绿色化的高效转型。 智慧电网利用物理模型、现场测量数据和历史数据&#xff0c;结合云计算、物联网、大数据等技术&#xf…

MiniMind - 从0训练语言模型

文章目录 一、关于 MiniMind &#x1f4cc;项目包含 二、&#x1f4cc; Environment三、&#x1f4cc; Quick Start Test四、&#x1f4cc; Quick Start Train0、克隆项目代码1、环境安装2、如果你需要自己训练3、测试模型推理效果 五、&#x1f4cc; Data sources1、分词器&am…

EasyCVR视频汇聚平台如何配置webrtc播放地址?

EasyCVR安防监控视频系统采用先进的网络传输技术&#xff0c;支持高清视频的接入和传输&#xff0c;能够满足大规模、高并发的远程监控需求。平台支持多协议接入&#xff0c;能将接入到视频流转码为多格式进行分发&#xff0c;包括RTMP、RTSP、HTTP-FLV、WebSocket-FLV、HLS、W…