Linux《进度条》

Linux《进度条》

在之前的Linux基础开发工具当中我们已经了解了vim、gcc、makefile等基本的开发工具,那么有了这些开发工具我们就可以来实现我们Linux旅程当中的第一个程序——进度条。相信通过该项目的实现能让你对vim等开发工具更加的熟悉。一起加油吧!!!


1.补充知识回车与换行 

在实现进度条的项目之前我们先要来了解关于回车和换行的基础补充知识,在了解之后才能让接下来的项目的编写更加的顺畅。

首先我们要了解的是回车和换行有什么区别,此时你可能就会疑惑这两个实现的效果不是一样的吗?我们在点击回车的时候不就是实现了换行了吗?

其实这两个实现的效果是不同的,只不过在现代的计算机当中基本将这两个概念不进行区分了,但在上个实际使用的老式打字机上换行自是将对应的纸向下移动,而要将实现回车还需要将指针移动回起始位置。

那通过以上的示例就可以理解在现代的计算机当中回车其实是 换行+回车 的,通过以下的老式键盘就可以看出这一特点

其实在之前在C语言当中使用的\n就是本质上就是实现了换行+回车的功能,\r实现的就是回车的功能。 


2.练手小程序——倒计时

以上我们了解了回车的本质,那么接下来就来通过一个倒计时的小程序来作为实现进度条项目之前的联手项目,不过在实现之前先要通过以下的代码得出一个规则。

首先来看以下的代码:

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

在以上的代码当中运行会出现什么样的现象呢?

通过运行形成可执行程序以上的代码输出的效果如下所示:

 

此时就可以看出首先会在显示器当中打印出hello world,之后在休眠5妙再让程序结束

再看以下的代码会有什么现象:

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

以上的代码相比原来的代码只是少了一个换行符\n,此时该程序运行的结果会和之前的一样吗?

通过将该程序形成可执行程序运行之后就可以看出和之前不同,该程序一开始没有将hello world输出到显示器上,而是到程序结束的时候才打印到显示器上,那么为什么会有这样的现象呢? 


 

要解答以上的问题就需要了解C语言当中进行输出的流程是怎么样的,其实在将对应的信息输出到显示器之前是会将数据先存储到缓冲区,之后再统一的将缓冲区内的数据刷新到显示器上,因此以上的两份代码再执行完printf("hello world\n")与printf("hello world")之后都是将执行完的结果存储到缓冲区当中,那么为什么第一份代码能直接将结果显示到显示器上呢?

这其实和其实和显示器行刷新的策略有关,当使用\n之后会将缓冲区当中的数据刷新到显示器上,而第二份代码当中未使用\n就使得程序缓冲区当中的数据需要在程序结束的时候才刷新到显示器上,这也是第二份代码形成的可执行程序在最后才会显示出来。

那么在第二份代码当中不使用\n但要使其与第一份代码执行出相同的效果,要使用什么样的方式呢?

在此就需要了解一个库函数fflush

在此fflush的作用就是将缓冲区当中的数据进行强制刷新。我们知道在C语言的程序启动时默认会打开stdin、stdout、stderr三个标准输入输出流,那么此时就可以将缓冲区当中的数据强制刷新到标准输出流stdout上。

改进的代码如下所示:

#include<stdio.h> #include <unistd.h> int main() { printf("hello world"); fflush(stdout); sleep(5); return 0; } 

以上代码形成可执行程序之后输出结果如下所示:


了解了缓冲区之后接下来就来尝试编写倒计时程序的代码,在此要求是从10开是倒计时,倒计时的数字在同一行内一直进行刷新,直到最后数字未0时结束 

那么接下来就很容易的能实现出以下的代码

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

以上的代码就是使用了以上我们学习的fflush在进行每一次的printf之后就进行强制的刷新,并且在每次输出一个数字之后使用\r进行回车,这样就可以使得每一秒显示器上会出现一个数字且在同一行内输出。但是以后的代码还有一个问题,我们来通过运行看看。

通过以下的输出就可以看出问题的所在是在输出时一开始打印的10是两位数,之后再进行打印的是一位数,这就会使得打印出的结果变为了90、80……

这个问题形成的原因是由于printf在输出时默认的是右对齐,此时要修改为左对齐只需要在printf的占位符%之后加上一个-2即可,这样就可以限定最小宽度为2

修改之后的代码如下所示:

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

输出结果如下所示:

3.进度条项目实现

通过以上的倒计时小程序我们完成了在实现进度条项目之前的练手,那么接下来就可以开始实现我们Linux学习当中的第一个项目,在此会实现两个版本的进度条项目,其中第一个版本的代码较为简单,但是不具有实用性,第二份的代码才具有实用性。

在实现代码之前首先要来明确我们的实现的项目的要求是什么,在此实现出的效果需大致如下所示:

首先是有一个进度条进行进度的推进直到进度条满为止,在进度条之后有一个显示当前进度的百分比数,之后有一个旋转的光标来表示当前进度条是在推进的。

 

3.1 v1版本

在实现该进度条项目时分为三个文件,在process.h内进行实现函数的声明,在process.c内实现对应的函数,在main.c使用实现的函数来实现进度条

那么接下来就先来实现第一个版本的进度条,在此创建一个函数process_v1,在该函数内实现对应的代码。在该版本当中的进度条只需要每秒使得进度条向后推进指定的进度即可,并且在进度条之后的百分数也显示对应的进度值。

 但在实现process_v1内的函数代码之前我们先要将该程序对应的makefile进行构建

makefile内的内容如下所示:

BIN=process.exe SRC=$(wildcard *.c) OBJ=$(SRC:.c=.o) CC=gcc $(BIN):$(OBJ) $(CC) -o $@ $^ %.o:%.c $(CC) -c $< .PHYNO:clean clean: rm -f $(OBJ) $(BIN) 

接下来就来实现第一个版本的进度条

首先在实现进度条要从0到100,那么就创建一个大小为101的字符数组来存储对应的进度数据,使用'#'来表示进度元素,接下来使用memset函数将数组buffer内的元素都初始化为0。 

由于要在进度条的最后使用一个动态的翻转来表明进度条是在运行的,那么在此就创建一个数组tmp,该数组内存储对应的字符,进度条每增长一位就将对应的状态字符改变,

在此字符串内使用\\是因为两个\\才会被转译为\

接下来创建一个变量cnt来表示当前进度条的进度值,使用while喜欢来实现0到100进度。在此过程中每次打印完字符串、进度条百分比、状态元素之后都要回车并且进行强制刷新。 

每次打印完之后将buffer数组内对应cnt下标位置元素修改为'#',再让cnt++

注:在此每次状态元素都使用cnt%len,这样就可以使得得到的下标一直在tmp字符串的范围内

以上使用的函数usleep和sleep类似也是进行休眠,不过单位是毫秒

完整代码如下所示:

#include"process.h" //函数实现 #define MAX 101 #define STYLE '#' void process_v1() { char buffer[MAX]; memset(buffer,0,sizeof(buffer)); const char* tmp="|/-\\"; int len=strlen(tmp); int cnt=0; while(cnt<=100) { printf("[%-100s][%d%%][%c]\r",buffer,cnt,tmp[cnt%len]); fflush(stdout); usleep(50000); buffer[cnt]=STYLE; cnt++; } printf("\n"); } 

编写完process_v1的代码之后接下来就使用make命令生成对应的可执行程序process.exe


 

执行process.exe效果如下所示: 

3.2 v2版本

在上述内容中,我们已经实现了进度条的第一个版本。尽管上述进度条程序确实能够运行,但问题在于我们上面实现的进度条速度是均匀的。这在现实中几乎是不可能的情况。更多时候,进度条会呈现出速度的变化。例如,在下载文件时,由于网速的影响,网速快时进度条移动得快,网速慢时进度条移动得慢。因此,接下来实现的第二个版本的进度条将更符合实际情况。

在此创建一个process_v2来表示该版本进度条的实现

该函数的参数有两个分别为总的数据值以及当前完成任务的数据值。在该函数的内部就需要实现cur占totle总量的百分比进度条,在此数据的变化就不在该函数内实现。

首先和之前的process_v1函数类似也是先创建一个大小为101的数组buffer来存储进度条的内容,再创建一个数组tmp来存储动态变化的字符。

之后和之前实现函数不同的是使用cnt变量的值来确定对应状态数组的下标,创建一个浮点数的变量count来统计当前进度条的百分比,再使用变量sum来统计进度条内#的个数

 

完整代码如下所示:

#define MAX 101 #define STYLE '#' void process_v2(double totle,double cur) { char buffer[MAX]; memset(buffer,0,sizeof(buffer)); const char* tmp="|/-\\"; int len=strlen(tmp); static int cnt=0; double count=cur*100/totle; int sum=(int)count; int i=0; for(;i<=sum;i++) { buffer[i]=STYLE; } printf("[%-100s][%.1lf%%][%c]\r",buffer,count,tmp[cnt]); fflush(stdout); cnt++; cnt%=len; } 

实现了process_v2函数之后接下来就需要在main.c当中实现一个具体的场景,假设在此实现的是文件的下载,下载的总量是1024mb,下载的速度是0.1mb每秒,那么接下来实现的函数就如下所示:

#include"process.h" double totle=1024.0; double speed=1.0; void DownLoad() { double cur=0; while(cur<=totle) { process_v2(totle,cur); usleep(10000); cur+=speed; } } int main() { //函数使用 //process_v1(); DownLoad(); return 0; } 

执行以上代码make之后形成的可执行程序,输出结果如下所示:

 

以上虽然实现的进度条能执行下去,但是以上还是匀速的,那么此时要使得呈现出来的进度条快慢是变化就需要使用随机数。

并且还将下载总的数据量的大小由mai函数内调用DownLoad时传参。

在此之前先在process.h内引用对应的头文件

#include"process.h" void DownLoad(double totle) { srand(time(NULL)); double speed=rand()%51; double cur=0; while(cur<=totle) { process_v2(totle,cur); usleep(10000); cur+=speed; } printf("\n"); } int main() { //函数使用 //process_v1(); DownLoad(10240); return 0; } 

以上代码此时就会存在一个问题就是最终cur的值可能会无法和totle正好匹配,那么此时最后进度条就无法显示到100%,因此接下来就要对这种情况进行处理。

在此只需要按照以上的方式处理即可实现最终的进度条到100%。 

以上实现的进度条以及基本满足我们的预期了,接下来就是对以上代码进行优化,在以上代码中DownLoad和process_v2的耦合度较高,在此我们可以修改为回调函数的方式来实现函数的调用。

并且在process_v2函数当中再添加一个变量来表示当中进度进行的操作是什么。

这样就可以实现上传等其他的操作

完整代码如下所示:

process.h

#pragma once #include<stdio.h> #include<string.h> #include<unistd.h> #include<time.h> #include<stdlib.h> //函数声明 void process_v1(); void process_v2(const char* s,double totle,double cur); 


process.c

#include"process.h" //函数实现 #define MAX 101 #define STYLE '#' void process_v2(const char* s,double totle,double cur) { char buffer[MAX]; memset(buffer,0,sizeof(buffer)); const char* tmp="|/-\\"; int len=strlen(tmp); static int cnt=0; double count=cur*100/totle; int sum=(int)count; int i=0; for(;i<=sum;i++) { buffer[i]=STYLE; } printf("[%s][%-100s][%.1lf%%][%c]\r",s,buffer,count,tmp[cnt]); fflush(stdout); cnt++; cnt%=len; } void process_v1() { char buffer[MAX]; memset(buffer,0,sizeof(buffer)); const char* tmp="|/-\\"; int len=strlen(tmp); int cnt=0; while(cnt<=100) { printf("[%-100s][%d%%][%c]\r",buffer,cnt,tmp[cnt%len]); fflush(stdout); usleep(50000); buffer[cnt]=STYLE; cnt++; } printf("\n"); } 

main.c

#include"process.h" typedef void(*call_t)(const char*,double,double); void DownLoad(double totle,call_t cb) { srand(time(NULL)); double speed=rand()%51; double cur=0; while(cur<=totle) { cb("下载中:",totle,cur); if(cur>=totle)break; usleep(1000); cur+=speed; if(cur>totle)cur=totle; } printf("\n"); } void UPLoad(double totle,call_t cb) { srand(time(NULL)); double speed=rand()%51; double cur=0; while(cur<=totle) { cb("上传中:",totle,cur); if(cur>=totle)break; usleep(1000); cur+=speed; if(cur>totle)cur=totle; } printf("\n"); } int main() { //函数使用 //process_v1() printf("下载总量:%d\n",10000); DownLoad(10000,process_v2); printf("下载总量:%d\n",222222); DownLoad(222222,process_v2); printf("下载总量:%d\n",99); DownLoad(99,process_v2); printf("下载总量:%d\n",5555); DownLoad(5555,process_v2); printf("下载总量:%d\n",1); DownLoad(1,process_v2); printf("上传总量:%d\n",20000); UPLoad(20000,process_v2); return 0; } 

 

以上代码运行结果如下所示:


 

以上就是本篇的全部内容了,希望通过本篇的学习能让你对这些基础的开发工具有更深的认识

Read more

OpenClaw下载安装配置|Windows安装流程|macOS 安装流程|Telegram 集成使用|飞书集成使用|常见坑和注意事项保姆级教程

OpenClaw下载安装配置|Windows安装流程|macOS 安装流程|Telegram 集成使用|飞书集成使用|常见坑和注意事项保姆级教程

🦞 OpenClaw 保姆级部署教程:Windows/macOS 全平台安装 + Telegram/飞书双端集成实战 作者:猫头虎AI | 阅读时间:约 25 分钟 | 难度:⭐⭐⭐☆☆ 因为🦞实在太火了,加上看多了AI相关的东西搞得人很焦虑,所以还是打算自己部署一下找点自我安慰吧🤷。先不说我能用来干啥,就想探一探它这么火的原因😮‍💨 在Windows中折腾了一天,终于算是初步跑通了,并配置了 Telegram 和 飞书 两个 channel。本文将完整记录从环境准备到多平台集成的全流程,帮你避开我踩过的所有坑。 OpenClaw 是一个适用于任何操作系统的 AI 智能体 Gateway 网关,支持 WhatsApp、Telegram、Discord、飞书等多种聊天应用。 📋 阅读目录 * 🦞 OpenClaw 保姆级部署教程:Windows/macOS 全平台安装 + Telegram/飞书双端集成实战 * 安装前准备

By Ne0inhk
时序数据库 Apache IoTDB:从边缘到云端Apache IoTDB 全链路数据管理能力、部署流程与安全特性解读

时序数据库 Apache IoTDB:从边缘到云端Apache IoTDB 全链路数据管理能力、部署流程与安全特性解读

时序数据库 Apache IoTDB:从边缘到云端Apache IoTDB 全链路数据管理能力、部署流程与安全特性解读 前言 大数据与物联网技术飞速发展的今天,时序数据呈现出爆发式增长的态势,从工业传感器的实时监控数据到智能设备的运行日志,从金融交易的时序记录到新能源汽车的工况数据,时序数据已成为企业数字化转型的核心资产,选择一款合适的时序数据库,直接关系到数据存储效率、分析能力与业务价值挖掘。 本文将从时序数据库的核心需求出发,结合大数据场景特点,通过与国外主流产品的对比分析,重点阐述 Apache IoTDB 在选型中的核心优势。 Apache IoTDB 介绍 Apache IoTDB 是由中科院软件所主导研发的开源时序数据库,专为物联网与工业大数据场景设计,以高性能、轻量级、易扩展为核心特点,通过三层数据模型与多级存储引擎,实现对海量时序数据的高效存储、快速查询与全生命周期管理,已成为国产化时序数据管理的标杆产品 ✅极致的性能表现:采用 TsFile 存储结构与多维索引体系,单节点每秒可处理数百万数据点写入,复杂聚合查询响应达毫秒级,结合差值编码等压缩算法,数据压缩

By Ne0inhk
程序员必看!VSCode Remote+cpolar让Mac和Linux无缝协作,代码随身走

程序员必看!VSCode Remote+cpolar让Mac和Linux无缝协作,代码随身走

文章目录 * 前言 * 1.WSL 环境下网络诊断 * 2.安装cpolar实现随时随地开发 * 3.配置公网地址 * 4.VsCode 远程连接开发环境 * 5.保留固定TCP公网地址 * 总结 前言 作为全栈开发者,你是否受够了在公司MacBook和家里Windows PC间同步开发环境的痛苦?VSCode Remote-SSH插件彻底终结了这种折腾——只需一次配置,就能让本地编辑器直接访问远程服务器的文件系统和终端,无论是修改Node.js后端还是调试Python脚本,都像在本地操作般流畅。我司前端团队通过这种方式,成功实现设计师用MacBook编辑React组件,后端工程师用Linux工作站调试API,代码实时同步且环境完全一致。 最让我惊喜的是VSCode Remote的端口转发功能。上次开发微信小程序时,后端服务运行在阿里云服务器,通过Remote-SSH转发3000端口后,本地IDE直接访问localhost就能调试接口,省去了配置Nginx反向代理的麻烦。而WSL2环境下的Docker容器,更是可以通过这种方式暴露给局域网其他设备,实现"Wi

By Ne0inhk

用 WSL 快速部署 Docker:Windows 下的轻量容器环境搭建指南

目录 为什么要使用Docker 部署 Docker 前置条件 一,使用wsl 搭建Docker需要的基础环境 一 ,开启 WSL 2 二,指定发行版安装 编辑 三,手动指定发行版安装  四,补充,搭建Docker环境,会遇到的问题,及其解决办法 五,验证安装  六,更换国内源(解决 Ubuntu 下载软件慢的问题) 一,为防止意外,提前备份 源文件 二,更新系统软件包 三,验证 WSL 版本(确认是 WSL2) 二,下载 docker安装包 一,下载 docker安装包 二,更新 wsl

By Ne0inhk