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

news2025/1/13 17:12:37

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.coloradmin.cn/o/1519572.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

‘sqlcmd‘不是内部或外部命令,也不是可运行的程序或批处理文件。

目录 一、问题 二、下载&安装sqlcmd 实用工具 三、验证 四、结果 一、问题 今天使用批处理文件执行SQLServer数据库的SQL语法时报错&#xff0c;提示sqlcmd不是内部或外部命令&#xff0c;也不是可运行的程序或批处理文件。&#xff0c;发生这个问题的原因是当前系统缺少…

【开发工具学习_Xshell介绍与安装】

开发工具学习_Xshell介绍与安装 开发工具学习_Xshell介绍与安装Xshell介绍Xshell安装 开发工具学习_Xshell介绍与安装 Xshell介绍 Xshell [1]是一个强大的安全终端模拟软件&#xff0c;它支持SSH1, SSH2, 以及Microsoft Windows 平台的TELNET 协议。Xshell 通过互联网到远程主…

107. 如何使用Docker以及Docker Compose部署Go Web应用

文章目录 一、为什么需要Docker&#xff1f;二、Docker部署示例1. 准备代码2. 创建Docker镜像3. 编写Dockerfile4. Dockerfile解析5. 构建镜像6. 通过镜像创建容器运行 三、分阶段构建示例四、附带其他文件的部署示例五、关联其他容器六、Docker Compose模式七、总结 本文将介绍…

Android Studio实现内容丰富的安卓图书管理系统

获取源码请点击文章末尾QQ名片联系&#xff0c;源码不免费&#xff0c;尊重创作&#xff0c;尊重劳动 项目编号060 1.开发环境android stuido jdk1.8 eclipse mysql tomcat 2.功能介绍 安卓端&#xff1a; 1.注册登录 2.查看图书列表 3.查看图书详情 4.评论图书&#xff0c; 5.…

复习C语言基础中的基础:C语言发展、C89 C99有何区别、C语言特点

参考《C程序设计&#xff08;第五版&#xff09;》&#xff08;谭浩强&#xff09;一书&#xff1a; 1. 发展、C89 C99 2. 特点 记得时不时回顾一下背景特点&#xff0c;加深对C语言的理解。

学古琴,万一学错了,还能改吗?

【古琴】学琴容易&#xff0c;改琴难&#xff1b; 错误的观念形成&#xff0c;需要大量的时间去调整大脑和肢体的动作&#xff1b; 当别人在学习新知识的时候&#xff0c;你却在改错误。 在古琴界因为门派和传承的关系&#xff0c;有大量不科学的毛病"&#xff0c;比如音…

HTML—CSS盒子模型(Box Model)

基本介绍&#xff1a; CSS处理网页时&#xff0c;HTML的每一个标签可以看作是一个盒子&#xff0c;网页布局将指定的标签放到指定的位置上摆放&#xff0c;相当于摆放盒子。 每一个标签(盒子)所包含的内容&#xff1a;从外到内 ①外边距(margin)—规定盒子与盒子之间的距离&…

【ShenYu源码阅读】支持提醒通知设计,来看看开源贡献者都做了什么

相信大家碰到源码时经常无从下手&#x1f643;&#xff0c;不知道从哪开始阅读&#xff0c;面对大量代码晕头转向&#xff0c;索性就读不下去了&#xff0c;又浪费了一次提升自己的机会&#x1f62d;。 我认为有一种方法&#xff0c;可以解决大家的困扰&#xff01;那就是通过阅…

百度飞桨大模型训练营:人工智能与大语言模型

1 人工智能基础概念全景介绍 1.1 人工智能概念 解释这些概念 AI是计算机学科下的一个分支学科&#xff0c;旨在使用计算机使之能像人类一样学习和思考问题机器学习是AI的一个子集&#xff0c;它的一个重要特点就是不需要人去做显示编程&#xff08;不用手撕函数&#xff09;&…

YOLOv7_pose-Openvino和ONNXRuntime推理【CPU】

纯检测系列&#xff1a; YOLOv5-Openvino和ONNXRuntime推理【CPU】 YOLOv6-Openvino和ONNXRuntime推理【CPU】 YOLOv8-Openvino和ONNXRuntime推理【CPU】 YOLOv7-Openvino和ONNXRuntime推理【CPU】 YOLOv9-Openvino和ONNXRuntime推理【CPU】 跟踪系列&#xff1a; YOLOv5/6/7-O…

关系代数-练习

设有一个SPJ数据库&#xff0c;包括4个关系模式S、P、J和 SPJ。 S(SNO, SNAME,STATUS,CITY) ; P(PNO,PNAME,COLOR,WEICHT) ; J(JNO,JNAME,CITY); SPJ(SNO,PNO,JNO,QTY)。 供应商表S由供应商代码(SNO)、供应商姓名(SNAME)、供应商状态(STATUS)、供应商…

第二十四节 Java 异常处理

什么是异常&#xff1f; 程序运行时&#xff0c;发生的不被期望的事件&#xff0c;它阻止了程序按照程序员的预期正常执行&#xff0c;这就是异常。异常发生时&#xff0c;是任程序自生自灭&#xff0c;立刻退出终止&#xff0c;还是输出错误给用户&#xff1f;或者用C语言风格…

【智能算法】人工水母搜索算法(JS)原理及实现

目录 1.背景2.算法原理2.1算法思想2.2算法过程 3.代码实现4.参考文献 1.背景 2020年&#xff0c;Chou 等人受到水母运动行为启发&#xff0c;提出了人工水母搜索算法(Artificial Jellyfish Search Optimizer, JS)。 2.算法原理 2.1算法思想 JS模拟了水母的搜索行为&#xf…

搭建一个自己的AI学术语音助手(一)

背景&#xff1a; 大模型出来后语音助手借着LLM的语义理解、知识组织能力的提升&#xff0c;升级了一波buffer。然后在使用这些语音助手的时候总觉得缺了点什么&#xff0c;但也讲不出来具体缺了什么。这几天的思考突然有了灵感&#xff0c;其实缺的就是自己的知识内容如何变成…

细说C++反向迭代器:原理与用法

文章目录 一、引言二、反向迭代器的原理与实现细节三、模拟实现C反向迭代器反向迭代器模板类的设计反向迭代器的使用示例与测试 一、引言 迭代器与反向迭代器的概念引入 迭代器&#xff08;Iterator&#xff09;是C标准模板库&#xff08;STL&#xff09;中的一个核心概念&am…

LinuxU盘挂载原理,为什么要用到U盘挂载及实现U盘挂载

目录 一、U盘挂载原理 二、为什么要用到U盘挂载 三、实现U盘挂载 一、准备工作 1、安装gcc 2、下载ntfs-3g 3、解压 4、编译准备 5、编译并安装 二、挂载演示 一、U盘挂载原理 Linux的U盘挂载原理涉及以下几个方面&#xff1a; 设备识别&#xff1a;当您将U盘插入Lin…

力扣L10--- 3. 无重复字符的最长子串--2024年3月14日

1.题目 2.知识点 注1&#xff1a;containsKey 是 Java 中 HashMap 类的一个方法&#xff0c;用于检查哈希表中是否包含指定的键。 注2&#xff1a;在哈希表&#xff08;HashMap)中&#xff0c;每个键对应着唯一的值&#xff0c;因此键不能重复&#xff0c;但值可以重复。 (1)创…

股价暴涨20%,BOSS直聘最新财报透露四点关键信息

又是一年招聘季&#xff0c;随着“金三银四”的到来&#xff0c;招聘市场再度成为话题讨论的中心&#xff0c;无数求职者踏入劳动力市场。 对比起曾经线下场场爆满的人才市场、双选会&#xff0c;今年就业市场的热闹场景出现在线上&#xff0c;2月28 日&#xff0c;“智联招聘…

Python面试笔记

Python面试笔记 PythonQ. Python中可变数据类型与不可变数据类型&#xff0c;浅拷贝与深拷贝详解Q. 解释什么是lambda函数&#xff1f;它有什么好处&#xff1f;Q. 什么是装饰器&#xff1f;Q. 什么是Python的垃圾回收机制&#xff1f;Q. Python内置函数dir的用法&#xff1f;Q…

10、MongoDB -- MongoDB 的 MongoTemplate 的功能和用法介绍

目录 MongoTemplate 的功能和用法演示前提&#xff1a;登录单机模式的 mongodb 服务器命令登录【test】数据库的 mongodb 客户端命令登录【admin】数据库的 mongodb 客户端命令 为 MongoDB 提供的两个 Starterspring-boot-starter-data-mongodb&#xff08;为以同步方式操作 Mo…