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*40
   
const char* str="|/-\\"; // 旋转光标

void process()
{
    // version 1
    int 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 1
    int 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 2
    static 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.mfbz.cn/a/460395.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

js【详解】ajax (含XMLHttpRequest、 同源策略、跨域、JSONP)

ajax 的核心API – XMLHttpRequest get 请求 // 新建 XMLHttpRequest 对象的实例 const xhr new XMLHttpRequest(); // 发起 get 请求&#xff0c;open 的三个参数为&#xff1a;请求类型&#xff0c;请求地址&#xff0c;是否异步请求&#xff08; true 为异步&#xff0c;f…

数据结构(二)顺序表和链表

1.线性表 线性表&#xff08;linear list&#xff09;是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使 用的数据结构&#xff0c;常见的线性表&#xff1a;顺序表、链表、栈、队列、字符串... 线性表在逻辑上是线性结构&#xff0c;也就说是连续的一条直…

fiddle连接mumu模拟器到adb连接成功,保姆级

前言: 在现代的移动应用程序开发中&#xff0c;模拟器成为了一个必不可少的工具。而Mumu模拟器是一个非常受欢迎的选择&#xff0c;它提供了稳定的性能和丰富的功能。然而&#xff0c;要在模拟器上进行调试和测试&#xff0c;你需要将它与ADB连接起来。 首先&#xff0c;我将解…

网络层_IP

传输层解决的是传输控制&#xff0c;而实际真正决定数据能否发送到对端的是网络层。网络层是有概率传输&#xff0c;而传输层是可靠性传输。所以传输层网络层就可以做到将数据可靠发送到对端。网络层的常见协议有&#xff1a;IP、ICMP等&#xff0c;其中最重要的是IP协议&#…

el-table的border属性失效问题解决方案

目录 问题&#xff1a; 使用的代码&#xff1a; 官方文档的说明&#xff1a; 可能的问题所在&#xff1a; 关于使用了作用域插槽&#xff1a; a.自定义内容的样式覆盖&#xff1a; b.表格结构的改变&#xff1a; 解决方案&#xff1a; 通过css样式解决&#xff1a; 下面…

Unity AI Navigation插件快速使用方法

AI Navigation插件使您能够创建能够在游戏世界中智能移动的角色。这些角色利用的是根据场景几何结构自动生成的导航网格。障碍物可以让您在运行时改变角色的导航路径。 演示使用的Unity版本为Tuanjie 1.0.0,团结引擎是Unity中国的引擎研发团队基于Unity 2022 LTS版本为中国开发…

HTML、XHTML和HTML5系列对比

目录 HTML HTML的优点&#xff1a; HTML的缺点&#xff1a; 应用场景&#xff1a; XHTML XHTML的优点&#xff1a; XHTML的缺点&#xff1a; 应用场景&#xff1a; HTML5 HTML5的优点&#xff1a; HTML5的缺点&#xff1a; 应用场景&#xff1a; 回首发现&#xff0…

面试经典-31-随机链表的复制

题目 给你一个长度为 n 的链表&#xff0c;每个节点包含一个额外增加的随机指针 random &#xff0c;该指针可以指向链表中的任何节点或空节点。 构造这个链表的 深拷贝。 深拷贝应该正好由 n 个 全新 节点组成&#xff0c;其中每个新节点的值都设为其对应的原节点的值。新节…

C语言- strcat(拼接函数的使用和模拟)

strcat&#xff08;拼接函数的使用和模拟&#xff09; strcat的语法 strcat 是 C 语言标准库中的一个字符串拼接函数&#xff0c;它用于将一个字符串&#xff08;source&#xff09;拼接到另一个字符串&#xff08;destination&#xff09;的末尾。该函数定义在 <string.h…

Java开发从入门到精通(九):Java的面向对象OOP:成员变量、成员方法、类变量、类方法、代码块、单例设计模式

Java大数据开发和安全开发 &#xff08;一)Java的变量和方法1.1 成员变量1.2 成员方法1.3 static关键字1.3.1 static修饰成员变量1.3.1 static修饰成员变量的应用场景1.3.1 static修饰成员方法1.3.1 static修饰成员方法的应用场景1.3.1 static的注意事项1.3.1 static的应用知识…

基于暗通道的图像去雾算法,Matlab实现

博主简介&#xff1a; 专注、专一于Matlab图像处理学习、交流&#xff0c;matlab图像代码/项目合作可以联系&#xff08;QQ:3249726188&#xff09; 个人主页&#xff1a;Matlab_ImagePro-CSDN博客 原则&#xff1a;代码均由本人编写完成&#xff0c;非中介&#xff0c;提供有偿…

MongoDB简单CRUD操作(含GO中的库操作)

MongoDBCRUD操作&#xff08;含GO中的库操作&#xff09; 这周开始尝试做新项目&#xff0c;涉及到了文章的存储&#xff0c;查了查MongoDB在这方面用的比较多&#xff0c;因此对MongoDB和他在Golang中的用法进行了学习&#xff0c;以下是我的整理 文章目录 MongoDBCRUD操作&a…

IDEA中的Project工程、Module模块的概念及创建导入

1、IDEA中的层级关系&#xff1a; project(工程) - module(模块) - package(包) - class(类)/接口具体的&#xff1a; 一个project中可以创建多个module一个module中可以创建多个package一个package中可以创建多个class/接口2、Project和Module的概念&#xff1a; 在 IntelliJ …

vue3模块化引用组件和引用ts,调用ts中的接口

以简单的登录功能为例子 1.在util中创建loginValidators.ts import { ref, reactive } from vueinterface User{email: string;password: string; }export const loginUserreactive<User>({email: ,password: })interface Rules{email: {required: boolean;message: …

Html提高——HTML5 新增的语义化标签

引入&#xff1a; 以前布局&#xff0c;我们基本用 div 来做。div 对于搜索引擎来说&#xff0c;是没有语义的。 但是在html5里增加了语义化标签&#xff0c;如 <header>&#xff1a;头部标签 <nav>&#xff1a;导航标签 <article>&#xff1a;内容标签 &…

鸿蒙Harmony应用开发—ArkTS声明式开发(容器组件:ListItem)

用来展示列表具体item&#xff0c;必须配合List来使用。 说明&#xff1a; 该组件从API Version 7开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。该组件的父组件只能是List或者ListItemGroup。 子组件 可以包含单个子组件。 接口 从API…

[江苏工匠杯]easyphp

先看源码 <?php highlight_file(__FILE__); $key1 0; $key2 0; ​ $a $_GET[a]; $b $_GET[b]; ​ if(isset($a) && intval($a) > 6000000 && strlen($a) < 3){if(isset($b) && 8b184b substr(md5($b),-6,6)){$key1 1;}else{die("…

遗传算法及基于该算法的典型问题的求解实践

说明 遗传算法是一个很有用的工具&#xff0c;它可以帮我们解决生活和科研中的诸多问题。最近在看波束形成相关内容时了解到可以用这个算法来优化阵元激励以压低旁瓣&#xff0c;于是特地了解和学习了一下这个算法&#xff0c;觉得蛮有意思的&#xff0c;于是把这两天关于该算法…

大模型训练准备工作

一、目录 1 大模型训练需要多少算力&#xff1f; 2. 大模型训练需要多少显存&#xff1f; 3. 大模型需要多少数据量训练&#xff1f; 4. 训练时间估计 5. epoch 选择经验 6. 浮点计算性能测试 二、实现 1 大模型训练需要多少算力&#xff1f; 训练总算力&#xff08;Flops&…

分割等和子集 最后一块石头的重量II 目标和

416. 分割等和子集 力扣题目链接 本题中每一个元素的数值既是重量&#xff0c;也是价值。 dp[j]表示 背包总容量&#xff08;所能装的总重量&#xff09;是j&#xff0c;放进物品后&#xff0c;背的最大重量为dp[j] 如果背包容量为target&#xff0c; dp[target]就是装满 背…
最新文章