【Linux系列】让 Vim “跑”起来:建立一个会动的进度条

news/2025/10/1 14:18:10/文章来源:https://www.cnblogs.com/wzzkaifa/p/19122421

在这里插入图片描述

励志不掉头发的内向程序员:个人主页

 ✨️ 个人专栏: 《C++语言》《Linux学习》

偶尔悲伤,偶尔被幸福所完善


️博主简介:

在这里插入图片描述

文章目录

  • 前言
  • 一、回车与换行
  • 二、行缓冲区
  • 三、倒计时
  • 四、进度条
  • 总结


前言

我们已经了解了我们的 vim 编辑器,还明白了 gcc/g++ 编译器,同时了解了 Makefile 的自动化编译,本章节我们凭借这几个基本的开发工具来实现一个小程序,也是我们常见的进度条。我们一起来看看吧。

在这里插入图片描述


一、回车与换行

很多人可能认为回车与换行是一个概念,那就是跳转到下一行的开始,然后再去进行我们的操作,其实并不是这样的。

所以我们之前理解的回车或者说换行其实本质上是 回车+换行。

在这里插入图片描述
在计算机语言中,换行符一般为 " \n ",回车符一般为 " \r "。我们往往用 " \r\n " 来表示回车 + 换行。


二、行缓冲区

缓冲区的内容我们在这里就不过多的去了解,等我们知识储备量多一点的时候再去具体说明,此时我们只要把它当成一个特殊一点的内存块即可。

我们先来看看这一串代码:

#include <stdio.h>#include <unistd.h>int main(){printf("hello world!\n");sleep(3);return 0;}

此时输出 hello world 的时候我们让程序休眠 3s。

注意:
sleep 函数存在 unistd.h 的头文件中。它的作用是让程序休眠一定时间,参数为要休眠的时间,单位为秒。

运行时可以看见,先打印 hello world 后等待 3s 程序退出。这很简单,没有必要去讲。此时我们把 “ \n ” 去掉的现象是什么呢?
在这里插入图片描述
在这里插入图片描述
我们发现当我们运行程序时,没有第一时间打印我们的内容,而是等待 3s 后程序停止时一起显示出来。看上去好像是先执行 sleep 再执行 printf,但是我们的常识告诉我们这不太可能。程序在执行时永远都是从前往后执行的。也就是说我们把 printf 执行完了,但是显示器上却没有显示内容。在计算机休眠的时候,hello world 在哪里呢?其实它们刚开始时数据是存储在计算机的缓冲区中的。之前有 “ \n ”就会先显示出来是因为我们 " \n " 是我们缓冲区的一个刷新条件,也叫行刷新。

我们刷新缓冲区的条件一共有两中:

  • 行刷新:编译器在识别到 " \n " 时会刷新缓冲区。
  • 程序退出:当我们程序退出时会自动刷新我们的缓冲区。

这就是为什么我们结束的时候 hello world 会和命令行一起出现。如果想要我们的缓冲区立刻刷新,我们可以使用 fflush 函数来让其立刻刷新。其实我们每次打开程序时都会默认打开 3 个文件,分别是 stdin、stdout、stderr, stdin 默认对应的是我们键盘文件,stdout、stderr 默认对应的是我们显示器文件。

注意:
fflush 函数的参数是你要刷新的文件,我们想要刷新显示器就是以 stdout 文件为参数去刷新。fflush 在 stdio.h 的头文件中。

int main()
{
printf("hello world!");
fflush(stdout);
sleep(3);
return 0;
}

此时我们再去运行就会发现 hello world 会先打印,然后程序再休眠。
在这里插入图片描述
在这里插入图片描述


三、倒计时

我们在实现进度条之前先来写一个倒计时的程序,通过这个倒计时进而去了解一些进度条需要用到的方法。

我们就来简单的写一个 10 ~ 1 的倒计时。

int main()
{
int i = 9;
while(i >= 0)
{
printf("%d\n", i);
i--;
}
return 0;
}

这个代码非常简单,没啥可说的。

zxl@iv-ye423qlwxsqc6ikwbogx:~/lesson1$ ./code
9
8
7
6
5
4
3
2
1
0

但是它输出出来是这样的一串,我们不希望输出一串,而是在同一个位置去打印。

我们可以发现,其实我们的输入的字符的位置在哪里本质上是跟着我的光标位置走的,我的光标哪里,我输入的字符也就会出现在那个位置,所以我们只需要让我们的光标在输入的时候位置一直不变,此时就能不停的覆盖我们之前输入的位置。这样不就实现了一个倒计时吗。此时我们用到上面的知识。只需要让我们的内容输入完,我们的光标回车即可,也就是 “ \r ”。

int main()
{
int i = 9;
while(i >= 0)
{
printf("%d\r", i);
sleep(1);
i--;
}
return 0;
}

此时运行时,由于没有满足缓冲区刷新条件,所以一直到程序结束都不会有数字出现。
在这里插入图片描述
在这里插入图片描述
手动刷新一下就行了。

printf("%d\r", i);
fflush(stdout);
sleep(1);
i--;

在这里插入图片描述
如果我们想要变成从 10 倒计时会怎样呢?
在这里插入图片描述
我们发现它从 10 变得 90,然后是 80…。这样打印肯定是错误的,我们解决方法也很简单,把我们输出的位宽改成 2d 即可。

printf("%2d\r", i);

在这里插入图片描述
如果想要让 9、8、7… 靠左边显示,再在前面加一个 “ - ” 即可。

printf("%-2d\r", i);

在这里插入图片描述

四、进度条

在有了我们倒计时的前置内容后,我们想要实现一个进度条也就变得简单了许多。

zxl@iv-ye423qlwxsqc6ikwbogx:~/lesson2$ touch process.c
zxl@iv-ye423qlwxsqc6ikwbogx:~/lesson2$ touch process.h
zxl@iv-ye423qlwxsqc6ikwbogx:~/lesson2$ touch main.c
zxl@iv-ye423qlwxsqc6ikwbogx:~/lesson2$ touch Makefile
main.c  Makefile  process.c  process.h
#Makefile
SRC=$(shell ls *.c)
OBJ=$(SRC:.c=.o)
BIN=processbar
$(BIN):$(OBJ)
gcc -o $@ $^
%.o:%.c
gcc -c $<
.PHONY:clean
clean:
rm -f $(BIN) $(OBJ)

以上便是我们的准备工作,此时我们变开始写一个进度条。先给我们进度条声明。

// process.h
#pragma once
#include <stdio.h>void process_v1();// process.c#include "process.h"void process_v1(){//....}// main.c#include "process.h"int main(){process_v1();return 0;}

以上便是我们实现进度条的基本框架,此时我们即可以来实现一个进度条了。
我们进度条的样式:[########### ][ 50.0% ][ \ ]

我们得创建一个101大小的字符串用来存储我们的 100 个进度(#),最后一个是 “ \0 ” 的位置。

#define NUM 101
#define STYLE '#'
void process_v1()
{
char buffer[NUM];
memset(buffer, 0, sizeof(buffer));
int cnt = 0;
while(cnt <= 100)
{
printf("[%s]\r", buffer);
fflush(stdout);
buffer[cnt] = STYLE;
cnt++;
sleep(1);
}
printf("\n");
}

此时我们就实现了一个简易的进度条主体部分。达到的效果就是随着时间增加,我们的进度条长度也会增加。
在这里插入图片描述
当然,像这样增长的进度条看上去就不是很好看,我们可以给他预留出一定的空间,这样它只是进度在增长,边框就不会增长。

printf("[%-100s]\r", buffer);

在这里插入图片描述
当然,我们的进度条还有百分比,此时我们在 printf 后面继续输出即可。

printf("[%-100s][%d%%]\r", buffer, cnt);

在这里插入图片描述
我们在加载的时候会碰到各种情况,就比如说我们的进度不变。此时我们如果发现进度条一直没有变化,那到底是卡了还是没有只是加载慢呢?所以我们还得加一个光标。

#define NUM 101
#define STYLE '#'
void process_v1()
{
char buffer[NUM];
memset(buffer, 0, sizeof(buffer));
const char *lable = "|/-\\";
int len = strlen(lable);
int cnt = 0;
while(cnt <= 100)
{
printf("[%-100s][%d%%][%c]\r", buffer, cnt, lable[cnt % len]);
fflush(stdout);
buffer[cnt] = STYLE;
cnt++;
usleep(65000);
}
printf("\n");
}

我们直接创建一个字符串,记录光标的四种形态,此时我们在运行时让我们的光标一直在这四种形态中循环即可。当然,这个进度条有点太慢了,所以这里用了一个 usleep() 函数,它和 sleep 的区别在于 sleep 的单位是秒,usleep 的单位是微秒。

在这里插入图片描述
进度条的实现就完成了,但是这个进度条是没法使用的,因为加入我们加入一个下载任务,这个进度条的加载没有什么意义。

一个进度条一定要结合具体的场景,边下载,边更新进度条才行。所以我们不能把进度条只写在一个文件中,而是要结合具体的情况来使用。

假设我们要下载一串数据,数据大小为 1024,网速为 1。

// main.c
double total = 1024.0;
double speed = 1.0;
void DownLoad()
{
double current = 0;
while(current <= total)
{
FlushProcess(total, current) // 刷新进度
// 下载代码
usleep(3000); // 充当下载数
current += speed;
}
printf("download %lfMB Done\n", current);
}
int main()
{
DownLoad();
return 0;
}

此时我们模拟了一个下载的任务,此时我们尝试运行就会发现,没有进度条,所以我们什么都不会显示出来。
在这里插入图片描述
此时我们得再去实现一个进度条 FiushProcess,这个进度条得有已下载数据的大小和数据大小两个参数,这样才能够实现进度条和下载的数据量同步的效果。

//process.h
void FlushProcess(double total, double current);
//process.c
void FlushProcess(double total, double current)
{
}

我们的进度条就没有必要去循环了,只需要对我们下载过来的数据进行计算百分比后,看看要输出多少进度即可。

void FlushProcess(double total, double current)
{
char buffer[NUM];
memset(buffer, 0, sizeof(buffer));
const char *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][%lf][%c]\r", buffer, rate * 100, lable[cnt]);
cnt++;
fflush(stdout);
}

我们的光标应该怎么设计呢,我们不能像之前一样设计,因为我们网络下载有可能下载的很慢,此时如果和之前一样用比值,就有可能会像卡着那样一直不变,所以我们可以写一个静态成员变量,使我们每次进入刷新进度条时都 ++,这样我们就能保证我们的光标一直在转了。
在这里插入图片描述


总结

以上便是我们实现的第一个小程序,进度条啦,虽然有很多新的知识点,但是都不是特别的难,主要就是熟悉熟悉我们的 vim 和 gcc/g++ 以及 Makefile 的使用。

在这里插入图片描述

坚持到这里已经很厉害啦,辛苦啦
ʕ • ᴥ • ʔ
づ♡ど

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

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

相关文章

网上自己建网站网站建设合同建设方注意事项

来源&#xff1a;国防科技要闻据悉&#xff0c;俄罗斯地面部队已完成“木船”&#xff08;Kungas&#xff09;机器人系统样机的国家试验&#xff0c;将于2020年开始部署部队&#xff0c;用于作战试验。发展背景自2010年以来&#xff0c;俄罗斯机器人系统进入快速发展时期。2014…

网站空间商是什么图书馆网络规划与设计

Problem: 354. 俄罗斯套娃信封问题 文章目录 思路解题方法复杂度Code 思路 这个问题可以转换为最长递增子序列&#xff08;Longest Increasing Subsequence&#xff0c;LIS&#xff09;问题。先对信封按宽度升序排序&#xff0c;当宽度相同时&#xff0c;按高度降序排序。然后在…

天津建设工程评标专家网站怎么用ps制作个人网站模板下载

31.《危险化学品安全管理条例》所称重大危险源,是指生产、储存、使用或者搬运危险化学品,且危险化学品的数量等于或者超过&#xff08;&#xff09;的单元(包括场所和设施)。 A.标准 B.一定量 C.临界量 答案&#xff1a;C 32.《危险化学品生产企业安全生产许可证实施办法》…

电子文件分类整理与双向同步 2025年10月1日

电子文件分类整理与双向同步 2025年10月1日一、网上包邮购买 极空间私有云Z4Pro+性能版钛金灰+希捷酷狼Pro16TBx4块二、在极空间Windows电脑客户端设置文件夹实时双向同步任务三、在极空间Windows电脑客户端设置自动挂…

手机wap购物网站模板上海建筑设计院招聘

内网工具对抗 首先&#xff0c;你需要分析&#xff1a; 1、安全工具是否有源代码 2、安全工具源代码逻辑复杂程度 3、当前源代码你是否有能力修改 其次&#xff0c;你需要考虑&#xff1a; 1、无源码或无能力修改 2、各种异常bug打包问题 3、修改打包后效果也不太好 故…

C++版搜索与图论算法 - 详解

C++版搜索与图论算法 - 详解pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco",…

达成设计卓越:全面解析 IC 设计中的验证之道

在集成电路(IC)设计流程中,验证(Verification) 是确保设计符合规格、功能正确的关键环节。本文以简洁明快的风格,深入探讨验证的重要性、常用方法与实践经验,帮助设计师构建高效可靠的验证流程。 1、验证的重要…

Typora 笔记迁移 Obsidian 图片链接转换

解决了Typora 笔记迁移 Obsidian 图片链接转换问题,实现了自动化`![Pasted image 20221223164738.png](./Database/Pasted image 20221223164738.png)--![[Pasted image 20221223164738.png]]`附件:Typora 笔记迁移 O…

【Java八股文】12-分布式面试篇 - 教程

【Java八股文】12-分布式面试篇 - 教程pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Mon…

Java 运行 Word 文档标签并赋值:从基础到实战

Java 运行 Word 文档标签并赋值:从基础到实战pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", &q…

上海配资网站开发广州开发区投资集团有限公司

都是copy的百度SDK文档&#xff0c;简单说说怎么用。1、没安装Python的参见此文&#xff1a;Python学习笔记系列 1 ——安装调试Python开发软件2、winr输入cmd打开命令行&#xff0c;输入&#xff1a;pip install baidu-aip&#xff0c;如下安装百度AI的模块。3、新建文本文档&…

词云组件

效果图: 组件代码:<template><div class="post"><div class="portal-title flex-h justify-between"><div class="flex-h"><img class="icon"…

域名 网站名称网站建设对电子商务的作用

&#x1f308;个人主页: 程序员不想敲代码啊 &#x1f3c6;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f44d;点赞⭐评论⭐收藏 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共…

网站制作的关键技术新乡手机网站建设电话

文章目录 一、JavaScript 逻辑运算符1、逻辑运算符 概念2、逻辑与运算符 &&3、逻辑或运算符 ||4、逻辑非运算符 !5、完整代码示例 一、JavaScript 逻辑运算符 1、逻辑运算符 概念 JavaScript 中的 逻辑运算符 的作用是 对 布尔值 进行运算 , 运算完成 后 的 返回值 也是…

没有网站也可以做外贸吗wordpress和万网

碰撞 Collision. [kəˈliʒən] 碰撞&#xff1b;冲突&#xff1b;&#xff08;意见&#xff0c;看法&#xff09;的抵触&#xff1b;&#xff08;政党等的&#xff09;倾轧 选择一个需要添加碰撞器的游戏对象后 Component->Physics- Unity一共为对象提供了6 种碰撞器&…

监控设备网站制作工作服厂家联系方式

目录 1. 更换yum源更新系统软件包&#xff1a; 1.1备份yum源 1.1.1创建备份目录&#xff1a; 1.1.2移动现有仓库配置文件到备份目录&#xff1a; 1.1.3验证备份&#xff1a; 1.2更换yum源 1.2.1添加yum源 1.2.2删除和建立yum缓存 1.3更新系统软件包 1.4 yum与dnf介绍…

2025 年超声波清洗机品牌最新权威推荐排行榜:龙门式 / 悬挂式 / 全自动等多类型设备厂家 TOP3 精选,助力企业精准选购

当前电子、机械、医疗、汽车、新能源等精密制造行业飞速发展,对零部件清洗的精度、效率及环保标准提出更高要求,超声波清洗机作为核心清洗设备,市场需求持续攀升。但市场上品牌繁杂,产品质量、技术水平与服务能力差…

树的统一迭代法

树的统一迭代法是一种比较通用的遍历方法,通过标记法来实现前序、中序、后序遍历,核心思想是通过栈中加入空指针来标记访问节点和处理节点的时机 树的递归遍历 递归遍历比较简单,只要完成模板,更改添加元素的位置代…

怎么seo网站排名wordpress搭建电影

1、自己准备训练语料文件 根据自己的业务场景准备训练数据&#xff0c;比如用户在商城上的同购行为序列或同浏览行为序列。 我们希望通过自己训练业务相关的语料word2vec模型来获得词嵌入、词相关性查询等。 1.1 准备语料库文件 # 示例&#xff1a;准备自己的一个大规模的语…

集团网站开发费用关键词优化的价格查询

1. 题目 给你一个 n 行 m 列的二维网格 grid 和一个整数 k。你需要将 grid 迁移 k 次。 每次「迁移」操作将会引发下述活动&#xff1a; 位于 grid[i][j] 的元素将会移动到 grid[i][j 1]。 位于 grid[i][m - 1] 的元素将会移动到 grid[i 1][0]。 位于 grid[n - 1][m - 1] …