【Linux】从零开始:编写你的第一个Linux进度条小程序

embedded/2025/1/17 1:47:11/

请添加图片描述

Linux相关知识点可以通过点击以下链接进行学习一起加油!
初识指令指令进阶权限管理yum包管理与vim编辑器GCC/G++编译器
make与Makefile自动化构建GDB调试器与Git版本控制工具

文章目录

  • 一、知识铺垫
    • 1.1 回车与换行概念
    • 1.2 缓冲区
  • 二、实现简单倒计时
  • 三、进度条
    • 3.1 Verrsion1:进度条实现
    • 3.2 下载场景
      • 3.2.1 Main.c
      • 3.2.2 Version2:进度条实现

在Linux编程的学习过程中,制作一个进度条小程序是入门的好选择。本教程将从零开始,带你轻松实现第一个Linux进度条,感受编程的乐趣与成就

一、知识铺垫

1.1 回车与换行概念

在这里插入图片描述

  • 回车】:回到该行首位置
  • 换行】:跳转到下一行

在编程中,转义字符\r表示回车符,将光标移动到行首,而\n表示换行符,将光标移动到下一行首。其中\n完成了两种行为:换行和回车。

1.2 缓冲区

在计算机中,缓冲区是一个临时存储区域,用于存放数据以便于处理。

在显示器上下文中,缓冲区主要指的是显存(视频内存),用于存储即将显示的图像或文本内容。这样,系统可以先将所有待显示的数据加载到缓冲区,然后一次性地将其刷新到屏幕上,以提高显示效率和流畅度。

在这里插入图片描述

二、实现简单倒计时

#include <stdio.h>
#include <unistd.h>int main()
{int cnt = 9;while (cnt >= 0){printf("%d\r", cnt);cnt--;sleep(1);}return 0;
}

实现一个简单动态效果的倒计时,动态效果就是在同一个位置不断数据进行刷新,达到动态效果。代码转义字符部分,不采用\n,而是\r。由于\n会换行,无法达到同一位置不断数据进行刷新的目的,而\r会在本行返回行首。

数据没有显示

由于存在 \r 转义字符,虽然数据会被打印出来,但光标会被移回到行的最左侧。这意味着后续打印的内容将覆盖当前行的开头部分。(打印-光标回到行首-打印覆盖)

fflush强制刷新

如果使用\r时数据没有显示,可能是因为终端的刷新机制。有时输出缓冲区没有立即刷新。你可以在每次打印后调用fflush(stdout);来强制刷新输出缓冲区。

#include <stdio.h>
#include <unistd.h>int main()
{int cnt = 10;while (cnt >= 0){printf("%2d\r", cnt);fflush(stdout); // 刷新输出缓冲区cnt--;sleep(1);}printf("\n倒计时结束!\n");return 0;
}

数据没有完全被覆盖】:由于字符10到字符0~9中,会出来一个字符没有被覆盖,这里可以通过在d前面填加数据,确保数据被覆盖完全。

三、进度条

设计进度条初步想法:有特殊字符表示进度和目前进度占比及其旋转光标。

在这里插入图片描述
在makefile文件
在这里插入图片描述
makefile文件中,头文件不用出现,头文件本身就在我的当前目前下,目录下进行编译,你的原文件已经证明了,#include这个源文件”是在当前目录下的,对此我们makefile文件不需要存在。@^就是代表整个依赖文件,中间需要空格进行隔开。

3.1 Verrsion1:进度条实现

“Version1是用于搭建大体轮廓”,并不是完善好的版本。

#include"Process bar.h"#define Length 101//设计缓冲区长度,其中为'\0'多开一块空间
#define Style '#'//设计进度条状态,显示特殊字符const char* lable = "|/-\\";
//version1
void ProcessBar()
{char bar[Length];//定义缓冲区memset(bar, '\0', sizeof(bar));//设置缓冲区初始数值,清空缓冲区数据操作int len = strlen(lable);int cnt = 0;while (cnt <= 100){printf("[%100s][%d%%][%c]\r", bar,cnt,lable[cnt%len]);fflush(stdout);bar[cnt++] = Style;usleep(10000);}printf("\n");
}

[\r、fflush部分]:前期工作,在代码部分进行注释说明。这里主要是针对循环体进行说明。首先是\rfflush使用上,该点倒计时有过分享,就是需要使用fflush刷新缓冲区数据,提前将数据进行打印,否则由于\r光标回退,新内容进行覆盖,出现无法观察到数据出现情况。

[usleep函数部分]

  • usleep函数,同sleep函数效果是一致的。由于使用sleep导致程序休眠时间过长,进度条特殊字符缓慢。

在这里插入图片描述

  • usleep在休眠的时间单位叫做:micro second微秒
  • 时间单位转换1秒等于1000毫秒 1毫秒等于1000微秒

[printf输出格式部分]

  • [%-100s]:如果是单纯%s,会导致右括号跟特殊字符增多,而跟着跑。这里选择预留空间,使得特殊字符在预留空间部分进行跑动。由于printf默认字符串输出为右对齐,特殊字符从右向左输出打印,可以使-表示左对齐,解决这个问题。
  • [%3d%%]:添加比例,确保进度条进度卡住,通过旋转光标得知,进度条还是进行。同时添加数字,确保有充足空间。

[const char *lable ="|/-\ \ "] :这里\需要使用\的字面值

3.2 下载场景

由于进度条需要配合场景进行使用,现在我们设计一个下载文件的场景。

3.2.1 Main.c

void download()
{double filesize = 100 * 1024 * 1024 * 1.0;//下载文件总大小double current = 0.0;//当前下载进度double bandwidth = 1024 * 1024 * 1.0;//带宽printf("download begin, current: %lf\n", current);while (current <= filesize){ProcessBar(filesize, current);usleep(100000);current += bandwidth;}printf("\ndownload done, filesize: %lf\n", filesize);
}int main()
{download();return 0;
}

在这里插入图片描述

这里提示信息会将部分进度条冲掉,可以采用\n换行打印。

3.2.2 Version2:进度条实现

#include "Processbar.h"#define Length 101//设计缓冲区长度,其中为'\0'多开一块空间
#define Style '#'//设计进度条状态,显示特殊字符const char* lable = "|/-\\";
//version2
void ProcessBar(double total,double current)
{char bar[Length];//定义缓冲区memset(bar,'\0', sizeof(bar));//设置缓冲区初始数值,清空缓冲区数据操作int len = strlen(lable);int cnt = 0;double rate = (100 * current) / total;//进度int loop_count = (int)rate;//循环次数while (cnt <= loop_count){bar[cnt++] = Style;}printf("[%-100s][%.1lf%%][%c]\r", bar, rate, lable[cnt % len]);fflush(stdout);
}

打印出现卡顿情况

由于ProcessBar在外部会被不断调用,导致多次从头开始打印。这里可以将printf("[%-100s][%.1lf%%][%c]\r", bar, rate, lable[cnt % len]);提取出来。在循环体内,将字符串拼接好,放到缓冲区中,根据外部比例,只需要每次打印一张静态的进度条。

不同风格的代码

如果以后我们想在不改变源代码的基础上,使用其他形式进度条。比如:图形界面版的进度条使用起来,这里可以将函数作为参数,只需要传递函数指针即可。

模拟网络是否良好,可以在外部调用不同大小文件。

在这里插入图片描述

在这里插入图片描述


在这里插入图片描述

以上就是本篇文章的所有内容,在此感谢大家的观看!这里是Linux笔记,希望对你在学习Linux旅途中有所帮助!


http://www.ppmy.cn/embedded/154526.html

相关文章

【 PID 算法 】PID 算法基础

一、简介 PID即&#xff1a;Proportional&#xff08;比例&#xff09;、Integral&#xff08;积分&#xff09;、Differential&#xff08;微分&#xff09;的缩写。也就是说&#xff0c;PID算法是结合这三种环节在一起的。粘一下百度百科中的东西吧。 顾名思义&#xff0c;…

第十章:电子表格软件Excel

文章目录&#xff1a; 一&#xff1a;界面 1.介绍 2.选项卡 2.1 开始 2.2 插入 2.3 布局 2.4 公式 2.5 数据 2.6 审阅 2.7 视图 2.8 开发工具 2.9 图表工具 二&#xff1a;基础 1.工作簿 2.工作表 3.单元格 4.宏 三&#xff1a;数据 1.数据类型 2.自动填充…

理解Spark中运行程序时数据被分区的过程

在Spark中&#xff0c;数据分区是指将数据集分割成多个小的子集&#xff0c;即分区&#xff0c;以便在集群的多个节点上并行处理&#xff0c;从而提高处理效率。以下通过一个具体例子来理解&#xff1a; 例子背景 假设要分析一个包含100万条销售记录的数据集&#xff0c;每条…

linux 端口转发工具rinetd

rinetd是一个轻量级TCP转发工具&#xff0c;简单配置就可以实现端口映射/转发/重定向。 (1) 源码下载 wget https://li.nux.ro/download/nux/misc/el7/x86_64/rinetd-0.62-9.el7.nux.x86_64.rpm(2) 安装rinetd rpm -ivh rinetd-0.62-9.el7.nux.x86_64.rpm (3) 编辑配置文件 …

正则表达式完全指南

# 正则表达式完全指南 正则表达式&#xff08;Regular Expression&#xff0c;简称 regex 或 regexp&#xff09;是一种强大的文本匹配和处理工具。它使用特定的语法规则来描述字符串的匹配模式&#xff0c;广泛应用于文本搜索、替换和数据验证等场景。 ## 1. 基础语法 ### 1.1…

Docker挂载点

在Docker中&#xff0c;挂载点用于将宿主机上的目录映射到容器内的目录&#xff0c;这样可以实现数据的持久化存储和容器之间的数据共享。通过挂载点&#xff0c;容器可以访问宿主机上的文件系统&#xff0c;同时也能够持久化数据&#xff0c;即使容器本身被销毁&#xff0c;数…

Android Room 报错:too many SQL variables (code 1 SQLITE_ERROR) 原因及解决方法

报错信息&#xff1a; android.database.sqlite.SQLiteException: too many SQL variables (code 1 SQLITE_ERROR): while compiling: SELECT * FROM points WHERE id IN (?,?,?,...,?,?,?)SQLiteException: too many SQL variables 通常是由于一次查询或插入的 SQL 语句…

我的年度总结

这一年的人生起伏&#xff1a;从曙光到低谷再到新的曙光 其实本来没打算做年度总结的&#xff0c;无聊打开了帅帅的视频&#xff0c;结合自己最近经历的&#xff0c;打算简单聊下。因为原本打算做的内容会是一篇比较丧、低能量者的呻吟。 实习生与创业公司的零到一 第一段工…