当前位置: 首页 > news >正文

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

相关文章:

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

进度条---命令行版本 回车换行 其实本质上回车和换行是不同概念&#xff0c;我们用一张图来简单的理解一下&#xff1a; 在计算机语言当中&#xff1a; 换行符&#xff1a;\n 回车符&#xff1a;\r \r\n&#xff1a;回车换行 这时候有人可能会有疑问&#xff1a;我在学习C…...

vscode 无法使用npm, cmd命令行窗口可以正常执行

解决方法&#xff1a; 执行命令获得命令的位置 get-command npm 得到如下 然后删除或者修改 npm.ps1文件 让其不能使用就行。然后重启vscode即可。 pnpm 同理即可 另外加速源 国内镜像源&#xff08;淘宝&#xff09;&#xff1a; npm config set registry https://regist…...

Leetcode 967 Numbers With Same Consecutive Differences

题意 给定n&#xff0c;代表整数的长度&#xff0c;给定k代表两个相邻数字之间的间隔。求所有的值构成的组合 题目链接 https://leetcode.com/problems/numbers-with-same-consecutive-differences/description/ 题解 dfs&#xff0c;有k位置要选&#xff0c;第一个位置我…...

node.js中实现token的生成与验证

Token&#xff08;令牌&#xff09;是一种用于在客户端和服务器之间安全传输信息的加密字符串。在Web开发中&#xff0c;Token常用于身份验证和授权&#xff0c;确保用户能够安全地访问受保护的资源。 作用与意义 身份验证&#xff1a;Token可以用来验证用户的身份&#xff0…...

[C++11]_[初级]_[工作线程如何监听主线程条件变量wait_for方法的使用]

场景 在开发多线程程序时&#xff0c;有时候需要启动一个线程来监听外部进程的执行情况&#xff0c;并且在指定时间如果还没运行结束就强制结束外部线程。那么C标准库有这种监听线程并能在超时时提示的方法吗&#xff1f; 说明 在C11的<condition_variable>里就可以用…...

Openstack持久存储-Swift,Cinder,Manila三者之间的区别

总结不易&#xff0c;给个三连吧&#xff01;&#xff01;&#xff01; 补充&#xff1a; 文件共享存储服务Manila 在OpenStack生态系统中&#xff0c;Cinder和Manila分别提供了两种不同类型的存储服务&#xff0c;类似于传统的SAN&#xff08;存储区域网络&#xff09;和NAS&…...

深度学习第三弹:python入门与线性表示代码

一、python入门 1.熟悉基础数据结构——整型数据&#xff0c;浮点型数据&#xff0c;列表&#xff0c;字典&#xff0c;字符串&#xff1b;了解列表及字典的切片&#xff0c;插入&#xff0c;删除操作。 list1 [1, 2, 3, 4, 5] for each in list1:print(each) print(list1[1…...

解决报错记录:TypeError: vars() argument must have __dict__ attribute

解决报错记录&#xff1a;manager_pyplot_show vars(manager_class).get(“pyplot_show“) TypeError: vars() argument must 1.问题引申 在pycharm中调用matplotlib函数批量绘制维度图时&#xff0c;抛出异常&#xff1a; manager_pyplot_show vars(manager_class).get(&…...

SpringBoot 原理篇(day14)

配置优先级 SpringBoot 中支持三种格式的配置文件&#xff1a; 配置文件优先级排名&#xff08;从高到低&#xff09;&#xff1a; properties 配置文件yml 配置文件yaml 配置文件 注意事项 虽然 springboot 支持多种格式配置文件&#xff0c;但是在项目开发时&#xff0c;推荐…...

Vscode辅助编码AI神器continue插件

案例效果 1、安装或者更新vscode 有些版本的vscode不支持continue,最好更新到最新版,也可以直接官网下载 https://code.visualstudio.com/Download 2、安装continue插件 搜索continue,还未安装的,右下脚有个Install,点击安装即可 <...

Type-C单口便携显示器-LDR6021

Type-C单口便携显示器是一种新兴的显示设备&#xff0c;它凭借其便携性、高性能和广泛的应用场景等优势&#xff0c;正在成为市场的新宠。以下是Type-C单口便携显示器的具体运用方式&#xff1a; 一、连接与传输 1. **设备连接**&#xff1a;Type-C单口便携显示器通过Type-C接…...

青少年编程与数学 02-006 前端开发框架VUE 19课题、内置组件

青少年编程与数学 02-006 前端开发框架VUE 19课题、内置组件 一、Transition<Transition> 组件基于 CSS 的过渡效果CSS 过渡 class为过渡效果命名CSS 的 transitionCSS 的 animation自定义过渡 class同时使用 transition 和 animation深层级过渡与显式过渡时长性能考量 J…...

腾讯云AI代码助手编程挑战赛 - 使用 JavaScript 构建一个简易日历

功能简介&#xff1a; 动态年份选择&#xff1a;用户可以通过下拉框选择从 2000 年到 2050 年的任意年份。全年日历生成&#xff1a;根据用户选择的年份&#xff0c;动态生成该年份的所有 12 个月份的日历。直观的 UI 设计&#xff1a;使用 CSS 美化日历外观&#xff0c;使日历…...

Xcode 正则表达式实现查找替换

在软件开发过程中&#xff0c;查找和替换文本是一项常见的任务。正则表达式&#xff08;Regular Expressions&#xff09;是一种强大的工具&#xff0c;可以帮助我们在复杂的文本中进行精确的匹配和替换。Xcode 作为一款流行的开发工具&#xff0c;提供了对正则表达式的支持。本…...

学习flv.js

前言 flv.js一款使用纯 JavaScript 编写的 HTML5 Flash 视频 (FLV) 播放器&#xff0c;无需 Flash&#xff01;&#xff01;&#xff01;flv.js 的工作原理是将 FLV 文件流转换为 ISO BMFF&#xff08;碎片 MP4&#xff09;片段&#xff0c;然后通过Media Source Extensions&l…...

FreePBX 17 on ubuntu24 with Asterisk 20

版本配置&#xff1a; FreePBX 17&#xff08;最新&#xff09; Asterisk 20&#xff08;最新Asterisk 22&#xff0c;但是FreePBX 17最新只支持Asterisk 21&#xff0c;但是21非LTS版本&#xff0c;所以选择Asterisk 20&#xff09; PHP 8.2 Maria DB (v10.11) Node J…...

【算法】算法大纲

这篇文章介绍计算机算法的各个思维模式。 包括 计数原理、数组、树型结构、链表递归栈、查找排序、管窥算法、图论、贪心法和动态规划、以及概率论:概率分治和机器学习。没有办法逐个说明,算法本身错综复杂,不同的算法对应着不同的实用场景,也需要根据具体情况设计与调整。…...

【MySQL】SQL菜鸟教程(一)

1.常见命令 1.1 总览 命令作用SELECT从数据库中提取数据UPDATE更新数据库中的数据DELETE从数据库中删除数据INSERT INTO向数据库中插入新数据CREATE DATABASE创建新数据库ALTER DATABASE修改数据库CREATE TABLE创建新表ALTER TABLE变更数据表DROP TABLE删除表CREATE INDEX创建…...

安装本地测试安装apache-doris

一、安装前规划 我的服务器是三台麒麟服务器,2台跑不起来,这是我本地的,内存分配的也不多。 fe192.168.1.13 主数据库端口9030访问 8Gbe192.168.1.13内存4G 硬盘50be192.168.1.14内存4G 硬盘50be192.168.1.12内存4G 硬盘5013同时安装的fe和be 。 原理:192.168.1.13 服…...

【Apache Paimon】-- 13 -- 利用 paimon-flink-action 同步 mysql 表数据

利用 Paimon Schema Evolution 核心特性同步变更的 mysql 表结构和数据 1、背景信息 在Paimon 诞生以前,若 mysql/pg 等数据源的表结构发生变化时,我们有几种处理方式 (1)人工通知(比如常规的使用邮件),然后运维人员手动同步到数据仓库中 (2)使用 flink 消费 DDL bi…...

Chapter03-Authentication vulnerabilities

文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...

零门槛NAS搭建:WinNAS如何让普通电脑秒变私有云?

一、核心优势&#xff1a;专为Windows用户设计的极简NAS WinNAS由深圳耘想存储科技开发&#xff0c;是一款收费低廉但功能全面的Windows NAS工具&#xff0c;主打“无学习成本部署” 。与其他NAS软件相比&#xff0c;其优势在于&#xff1a; 无需硬件改造&#xff1a;将任意W…...

智慧工地云平台源码,基于微服务架构+Java+Spring Cloud +UniApp +MySql

智慧工地管理云平台系统&#xff0c;智慧工地全套源码&#xff0c;java版智慧工地源码&#xff0c;支持PC端、大屏端、移动端。 智慧工地聚焦建筑行业的市场需求&#xff0c;提供“平台网络终端”的整体解决方案&#xff0c;提供劳务管理、视频管理、智能监测、绿色施工、安全管…...

【2025年】解决Burpsuite抓不到https包的问题

环境&#xff1a;windows11 burpsuite:2025.5 在抓取https网站时&#xff0c;burpsuite抓取不到https数据包&#xff0c;只显示&#xff1a; 解决该问题只需如下三个步骤&#xff1a; 1、浏览器中访问 http://burp 2、下载 CA certificate 证书 3、在设置--隐私与安全--…...

Rust 异步编程

Rust 异步编程 引言 Rust 是一种系统编程语言,以其高性能、安全性以及零成本抽象而著称。在多核处理器成为主流的今天,异步编程成为了一种提高应用性能、优化资源利用的有效手段。本文将深入探讨 Rust 异步编程的核心概念、常用库以及最佳实践。 异步编程基础 什么是异步…...

UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)

UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中&#xff0c;UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化&#xf…...

wpf在image控件上快速显示内存图像

wpf在image控件上快速显示内存图像https://www.cnblogs.com/haodafeng/p/10431387.html 如果你在寻找能够快速在image控件刷新大图像&#xff08;比如分辨率3000*3000的图像&#xff09;的办法&#xff0c;尤其是想把内存中的裸数据&#xff08;只有图像的数据&#xff0c;不包…...

【Linux手册】探秘系统世界:从用户交互到硬件底层的全链路工作之旅

目录 前言 操作系统与驱动程序 是什么&#xff0c;为什么 怎么做 system call 用户操作接口 总结 前言 日常生活中&#xff0c;我们在使用电子设备时&#xff0c;我们所输入执行的每一条指令最终大多都会作用到硬件上&#xff0c;比如下载一款软件最终会下载到硬盘上&am…...

使用SSE解决获取状态不一致问题

使用SSE解决获取状态不一致问题 1. 问题描述2. SSE介绍2.1 SSE 的工作原理2.2 SSE 的事件格式规范2.3 SSE与其他技术对比2.4 SSE 的优缺点 3. 实战代码 1. 问题描述 目前做的一个功能是上传多个文件&#xff0c;这个上传文件是整体功能的一部分&#xff0c;文件在上传的过程中…...

【WebSocket】SpringBoot项目中使用WebSocket

1. 导入坐标 如果springboot父工程没有加入websocket的起步依赖&#xff0c;添加它的坐标的时候需要带上版本号。 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId> </dep…...