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

news/2024/7/20 1:07:30 标签: 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.niftyadmin.cn/n/5430606.html

相关文章

哥斯拉流量webshell分析-->ASP/PHP

哥斯拉流量webshell分析 哥斯拉是继菜刀、蚁剑、冰蝎之后的又一个webshell利器&#xff0c;这里就不过多介绍了。 哥斯拉GitHub地址&#xff1a;https://github.com/BeichenDream/Godzilla 很多一线师傅不太了解其中的加解密手法&#xff0c;无法进行解密&#xff0c;这篇文章…

十三、软考-系统架构设计师笔记-层次式架构设计理论与实践

1、层次式体系结构概述 层次式体系结构概述 层次式体系结构设计将系统组成一个层次结构&#xff0c;每一层为上层服务&#xff0c;并作为下层客户。内部的层接口只对相邻的层可见&#xff0c;每一层最多只影响两层&#xff0c;只要给相邻层提供相同的接口&#xff0c;允许每层…

【课程作业】基于Java的车辆租赁管理平台/租车系统

功能介绍 平台采用B/S结构&#xff0c;后端采用主流的Springboot框架进行开发&#xff0c;前端采用主流的Vue.js进行开发。给师弟的课程作业 整个平台包括前台和后台两个部分。 前台功能包括&#xff1a;首页、车辆详情、车辆预订、用户中心模块。后台功能包括&#xff1a;车…

Ubuntu系统的安装及基础操作

目录 一、VMware虚拟机安装Ubuntu20.04过程 1、安装前的准备工作 2、VMware虚拟机创建Ubuntu操作系统 步骤一&#xff1a;以管理员的身份运行VMware虚拟机 步骤二&#xff1a;新建虚拟机 步骤三&#xff1a;选择类型配置 步骤四&#xff1a;选择安装客户机操作系统 步骤…

为什么你那么相信自己

认知局限 由于不能预见到事情可能的各种发展方向&#xff0c;会对我们所知道的事情将来可能的发展过于自信。 证实偏见是人们往往倾向于为自己的观点找理由&#xff0c;或者说只关注和自已的观点一致的证据&#xff0c;而不关注也不收集和自己的观点相抵触的证据。 规划的误…

The service already exists!怎么解决,Windows怎么安装/卸载服务?

问题描述 有时候,我们在Windows系统上安装服务时会遇到报错,The service already exists! 问题分析 这个报错说明此服务已经存在了,所以我们不能再次安装,但有时候我们明明是第一次安装,为什么也会报这个错误呢? 在Windows上注册服务通常需要使用命令行工具或者特定的…

一篇普通的生活周记

学习进度汇报&#xff1a; 这周主要是参考着视频敲完了一个vue2后台项目&#xff0c;主要是vue2element-ui,因为之前写项目的时候用过lay-ui&#xff0c;虽然是结合着node.js写的&#xff0c;但是大差不差&#xff0c;所以上手也很快。同时&#xff0c;学长发给我们了ruoyi项目…

P5635 【CSGRound1】天下第一

题目背景 天下第一的 cbw 以主席的身份在 8102 年统治全宇宙后&#xff0c;开始了自己休闲的生活&#xff0c;并邀请自己的好友每天都来和他做游戏。由于 cbw 想要显出自己平易近人&#xff0c;所以 zhouwc 虽然是一个蒟蒻&#xff0c;也有能和 cbw 玩游戏的机会。 题目描述 …