Linux从0到1——Linux第一个小程序:进度条

Linux从0到1——Linux第一个小程序:进度条

  • 1. 输出缓冲区
  • 2. 回车和换行的本质
  • 3. 实现进度条
    • 3.1 简单原理版本
    • 3.2 实际工程版本

1. 输出缓冲区


1. 小实验:

编写一个test.c文件,:

#include <stdio.h>
#include <unistd.h>int main()
{printf("你能看见我吗?\n");sleep(1);   // 暂停1秒return 0;
}

编译并执行:

在这里插入图片描述

先打印,然后暂停一秒结束程序,很好理解。

2. 发现问题:

修改test.c文件内容如下:

在这里插入图片描述

再次编译并执行:

请添加图片描述

发现运行可执行程序后,没有直接打印内容,而是隔了一秒钟,才打印,这好像和我们理解的不太一样,是怎么回事?

我们可以确定的是,一定是printf先执行的,因为C语言代码一定是从上到下运行的,但是现象是字符没有打印。所以,我们可以断定,printf其实早就运行了,只不过在sleep期间,字符串没有被显示出来。在sleep期间,字符串在输出缓冲区当中。

3. 缓冲区:

C/C++语言,会针对标准输出,给我们提供默认的缓冲区(stdout)。我们可以使用fflush函数,可以把一个流强制做刷新(标准输入输出流之后会讲,这里只需要知道它可以刷新输出缓冲区)。

验证:

在这里插入图片描述

编译并执行:

请添加图片描述

那为什么加\n的程序,不需要刷新缓冲区?这是因为\n是一种刷新策略,叫行刷新,默认就有刷新缓冲区的功能。


2. 回车和换行的本质


在这里插入图片描述

我们来写一个倒计时程序:

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

运行一下:

请添加图片描述

可以发现它是换行打印倒计时,但是我们想让它只在一行打印,并且覆盖掉前一秒的秒数,如何做?\n是换行加回车,我们现在的需求是只回车,不换行,可以通过\r实现,但是\r没有刷新缓冲区的功能。

修改如下:

在这里插入图片描述

运行一下:

请添加图片描述


3. 实现进度条


3.1 简单原理版本


1. 原理讲解:

我们期望的进度条形式如下:

请添加图片描述

进度条的风格是#,右侧有一个百分数,提示当前具体进度,还有一个旋转光标,可以确定当进度条不动时,进程是还在进行还是卡住了。

  • 进度条的实现:第一次输出#,第二次输出##,一次类推,#越来越多。为了实现图中结果,我们可以通过不断回车,然后覆盖之前的#实现,比如用##覆盖#
  • 旋转光标的实现:利用一个常量字符串实现,让字符在|/-\这几个字符按顺序不断转换,实现旋转效果。

2. 文件准备:

在这里插入图片描述

main.c文件是主函数所在文件,进度条的具体实现在process.c文件中,主函数文件通过头文件process.h调用进度条的实现函数,各个文件内容如下(最重要是process.c)。

  • process.c文件:
#include "process.h"                                                                                        
#include <unistd.h>   // sleep的头文件
#include <string.h>#define SIZE 101
#define MAX_RATE 100
#define STYLE '#'
#define STIME 1000*40const char* str="|/-\\"; // 旋转光标void process()
{// version 1int rate = 0;char bar[SIZE] = {0}; // 全部初始化成'\0'int num = strlen(str);while(rate <= MAX_RATE){printf("[%-100s][%d%%][%c]\r", bar, rate, str[rate%num]); // 打印百分号最好用%%fflush(stdout);usleep(STIME); // 单位是u秒bar[rate++] = STYLE;}printf("\n");
}
  • main.c文件:
#include "process.h"                                                   int main()
{process();return 0;
}
  • process.h文件:
#pragma once#include <stdio.h> void process();
  • 编辑Makefile

在这里插入图片描述


3.2 实际工程版本


无论是任何进度条,一定是和某种任务关联的!

  • process.c文件:
#include "process.h"const char* str="|/-\\"; // 旋转光标void process_v1()
{// version 1int rate = 0;char bar[SIZE] = {0}; // 全部初始化成'\0'int num = strlen(str);while(rate <= MAX_RATE){printf("[%-100s][%d%%][%c]\r", bar, rate, str[rate%num]); // 打印百分号最好用%%fflush(stdout);usleep(STIME); // 单位是u秒bar[rate++] = STYLE;}printf("\n");
}// 不能一次将进度条打印完毕,否则无法平滑的和场景结合
// 该函数,应该根据rate,自动的打一次
void process_v2(int rate)
{   // version 2static char bar[SIZE] = {0}; int num = strlen(str);if(rate <= MAX_RATE && rate >= 0){printf("[%-100s][%d%%][%c]\r", bar, rate, str[rate%num]); // 打印百分号最好用%%fflush(stdout);bar[rate] = STYLE;}if(rate == MAX_RATE) memset(bar, '\0', sizeof(bar));
}
  • process.h文件:
#pragma once#include <string.h>
#include <stdio.h>
#include <unistd.h>#define SIZE 101
#define MAX_RATE 100
#define STYLE '#'
#define STIME 1000*40// 定义了一个函数指针类型,其中函数的参数是int,返回值是void
typedef void callback_t(int);void process_v1();
void process_v2(int);
  • main.c文件:
#include "process.h"#define TARGET_SIZE 1024*1024 // 1MB,下载软件总大小
#define DSIZE 1024*10 // 模拟每次下载的单位大小// 下载软件
void download(callback_t cb)
{int target = TARGET_SIZE; // 下载软件总大小int total = 0; // 目前下载了多少while(total < target){usleep(STIME); // 用简单的休眠时间,模拟本轮下载花费的时间total += DSIZE;int rate = total*100/target;cb(rate); // 传一个比率}printf("\n");
}int main()
{download(process_v2);return 0;
}

我们希望进度条在进度条函数内部循环打印,所以我们采用回调的方式,来进行某种任务的通知,动态更新进度条!


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

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

相关文章

记录一次难搞的编译错误-- qml-rust 项目编译无法找到QtCore库的问题

记录一次难搞的编译错误-- qml-rust 项目编译时无法找到QtCore库的问题 背景开始处理问题1 看日志找关键点2 写最简单测试用例3 写最简单CMake编译的Qt4 发现问题5 开始处理错误过程 1过程2过程3 反思开始找资料&#xff0c;解决自己的困惑。第二次开始解决问题第三次解决问题总…

升入理解计算机系统学习笔记

磁盘存储 磁盘是广为应用的保存大量数据的存储设备&#xff0c;存储数据的数量级可以达到几百到几千千兆字节&#xff0c;而基于RAM的存储器只能有几百或几千兆字节。不过&#xff0c;从磁盘上读信息的时间为毫秒级&#xff0c;比从DRAM读慢了10万倍&#xff0c;比从SRAM读慢了…

Discourse 分类图片

我们可以在 Discourse 上为分类添加图片。 进入分类编辑界面&#xff0c;然后选择 Image 标签。 在 Images 标签下&#xff0c;上传分类需要的图片。 图片大小 图片的大小是 Discourse 进行控制的&#xff0c;高度为 150 PX 像素。 如果上传的图片大于 150 px 的高度像素&…

智慧城市:提升城市治理能力的关键

目录 一、智慧城市的概念及特点 二、智慧城市在提升城市治理能力中的应用实践 1、智慧交通&#xff1a;提高交通治理效率 2、智慧政务&#xff1a;提升政府服务水平 3、智慧环保&#xff1a;加强环境监测与治理 4、智慧安防&#xff1a;提高城市安全水平 三、智慧城市在…

STM32F103 CubeMX 使用USB生成鼠标设备

STM32F103 CubeMX 使用USB生成鼠标设备 1 配置cubeMX1.1配置外部晶振&#xff0c;配置debug口1.2 配置USB1.3 配置芯片的时钟1.4 生成工程 2. 编写代码2.1 添加申明2.2 main函数代码 1 配置cubeMX 1.1配置外部晶振&#xff0c;配置debug口 1.2 配置USB 1.3 配置芯片的时钟 需…

Vulnhub - Jarbas

希望和各位大佬一起学习&#xff0c;如果文章内容有错请多多指正&#xff0c;谢谢&#xff01; 个人博客链接&#xff1a;CH4SER的个人BLOG – Welcome To Ch4sers Blog Jarbas 靶机下载地址&#xff1a;https://www.vulnhub.com/entry/jarbas-1,232/ 0x01 信息收集 Nmap…

openssl3.2 - note - Decoders and Encoders with OpenSSL

文章目录 openssl3.2 - note - Decoders and Encoders with OpenSSL概述笔记编码器/解码器的调用链OSSL_STORE 编码器/解码器的名称和属性OSSL_FUNC_decoder_freectx_fnOSSL_FUNC_encoder_encode_fn官方文档END openssl3.2 - note - Decoders and Encoders with OpenSSL 概述 …

从0开始启动一个Django的docker服务

本文是从0开始启动一个Django的docker服务&#xff0c;包括构建镜像,uwsgi启动服务 在服务器上安装ssh&#xff0c;git&#xff0c;生成公钥并复制到服务器上 # 安装ssh yum install openssh-clients # 生成sshkey ssh-keygen # 查看公钥 cat /root/.ssh/id_rsa.pubclone一下…

腾讯地图的(地图选点|输入模糊匹配)

1.支持用户输入框输入进行模糊匹配获取详细地址以及经纬度2.支持用户模糊匹配后点击选点获取详细地址以及经纬度 1.支持用户输入框输入进行模糊匹配获取详细地址以及经纬度2.支持用户模糊匹配后点击选点获取详细地址以及经纬度 <template><div class"tencentMap-…

【类和对象】类的作用域 | 类的实例化 | 类对象模型 | this指针

目录 5.类的作用域 6.类的实例化 6.1成员的声明和定义 6.2实例化出的对象大小 7.类对象模型❗❗ 7.1如何计算类对象的大小 7.2类对象的存储方式猜测 7.3结构体内存对齐规则 7.3.1内存对齐 7.3.2大小端 8.this指针 8.1this指针的引出 8.2this指针的特性 C和C实…

【网络安全】 MSF提权

本文章仅用于信息安全学习&#xff0c;请遵守相关法律法规&#xff0c;严禁用于非法途径。若读者因此作出任何危害网络安全的行为&#xff0c;后果自负&#xff0c;与作者无关。 环境准备&#xff1a; 名称系统位数IP攻击机Kali Linux6410.3.0.231客户端Windows 76410.3.0.234…

CentOS 7 socat命令端口转发 —— 筑梦之路

命令简介 socat是一个功能强大的命令行工具&#xff0c;也可以看作是netcat的加强版&#xff0c;它可以在两个端口之间建立虚拟通道&#xff0c;实现数据的传输。适用于网络调试、端口转发、安全测试等多种场景&#xff0c;是一个适合网络管理员和开发者的工具 yum在线安装 yu…