【Linux系统编程】进度条的编写

简介: 【Linux系统编程】进度条的编写

一,进度条的必备知识

1,缓冲区的粗略介绍

       缓冲区是内存的一部分空间,用于临时存储输入和输出的数据。它可分为输入缓冲区和输出缓冲区。每当我们输入数据时都是往输入缓冲区中存放数据,当刷新缓冲区时,数据将会从缓冲区中拿出输入到某个变量中。每当我们输出数据时,系统将会把数据输出到输出缓冲区中,当刷新输出缓冲区时,数据将会从输出缓冲区输出到指定地方。


       其中,缓冲区的刷新时机是不同的。行缓冲会在遇到换行符时刷新,全缓冲会在缓冲区写满时刷新,而无缓冲则没有缓冲区,代表是系统调用。在C/C++中,通常用 fflush(FILE* stream) 来强制刷新指定流的缓冲区。


       C/C++中类似于sleep函数功能控制的就是缓冲区,当系统调用到sleep是,将会被缓冲区暂时保存起来,一旦sleep运行完毕之后缓冲区才刷新。进度条有时控制的就是缓冲区的刷新时间。


2,回车与换行

       “ 回车 ” 是把光标从当前位置直接指向最开头位置。“ 换行 ” 是把光标从当前位置直接指向下一行同一列的位置。我们在C语言阶段常用的 “ \n ” 指的是 换行 + 回车。而 “ \r ” 只表示回车。


二,进度条的初步制作

1,进度条的初步矿建

       首先,我们先来编写进度条的简单倒计时程序,这就需要运用回车和sleep来控制程序的运行。


#include <iostream>
#include <iomanip> //setw的头文件
#include <unistd.h> //usleep()的头文件,对应参数单位为微秒                                             
#include <cstdio>
using namespace std;
int main()
{
    int n = 10;
    while (n >= 0) {
        cout << left << setw(2) << n << '\r'; //跟C语言中printf("%-2d\r", n)效果一样
        fflush(stdout);   //强制刷新输出缓冲区
        n--;
        usleep(500000);    //这里我们控制缓冲时间为0.5秒
    }
    cout << endl;
    return 0;
}


       下一步,要思考进度条的框架设计。这里的进度条将外围用 " = " 表示进度的加载,外围设置了百分比显示加载数据。用 "|/-\" 来表示其中的加载,即顺时针旋转。


2,进度条的版本一

       首先,外面设置一个头文件 "process.h" 进行必要的设置


#include <iostream>
#include <string>
#include <unistd.h>
#include <iomanip>
#include <cstdio>
using namespace std;
#define Body '='   //使用body来表示进度
#define Head '>'   //Head表示目前加载的终点,这里用 ' > ' 表示
void process1();  //进度条函数


       下面,进行进度功能的编写。这里使用 usleep 功能来控制进度的的运行,这里需注意的是输出缓冲区的刷新。


void process1()
{
    //用lable表示进度条的加载
    string lable("|/-\\");  
    string nums;
    int count = 0;
    int lablesize = lable.size();
    nums.push_back(Head);
    while (count <= 100)
    {
        cout << "[" << left << setw(100) << nums << "]";
        cout << "[" << "%" << count << "]";
        cout << "[" << lable[count % lablesize] << "]" << '\r';
        fflush(stdout);
        nums.clear();
        count++;
        nums.append(count, Body);
        if (count < 100)
        {
            nums.push_back(Head);
        }
        //这里我们设置每0.6秒加载一次
        usleep(60000); 
    }
    cout << endl;
}

运行最终结果:


[====================================================================================================][%100][|]

3,进度条的版本二

       进度条一般都是运用在一种应用上,表示应用的加载过程。很显然,版本一的进度条只是无脑运行,不知道程序进度是多少,即没有依附应用进度,比如下载程序,这时的进度条需依附于下载进度来跟进。


       头文件 "process.h" 添加如下:


#include <iostream>
#include <string>
#include <unistd.h>
#include <iomanip>
#include <cstdio>
#include <cstdlib>
using namespace std;
#define Body '='
#define Head '>'
#define Max 103
#define FileSize 1024*1024*1024  //设置FileSize文件内存为1G,表示下载程序的总大小
typedef void (*callback_t)(double);  //利用函数指针来进行封装进度运用
void download(callback_t);  //模拟一种下载进度
void process2(double rate); //进度条跟进程序

       这里,在设置download下载时要将每一次的下载进度传递给进度条让其显示百分比。


void download(callback_t cb)   //利用回调函数的形式设置进度
{
    srand(time(0)*1024);
    int total = FileSize;
    while (total)
    {
        //下面表示一次下载动作
        usleep(10000);
        int one = rand() % (1024 * 1024 * 5);
        total -= one;
        if (total < 0) 
        {
            total = 0;
        }
        //表示当前的进度
        int download = FileSize - total;
        double rate = (download * 1.0 / (FileSize)) * 100.0;
        cb(rate); //每一次进度条的传递
    }
}


进度条设置时要说明以下几点:


       1,我们使用 "|/-\\" 表示进度跟进时是根据下载进度进行的,与当前的进度无关。


       2,进度条的总设置需与下载程序紧紧联系。比如当程序加载完时,“ > ” 进度条中表示进          度运行的就要停止,即删除。


       3,在输出进度运行过程,我们可添加其色彩表示美观,链接:色彩文本的增添


void process2(double rate)
{
    //用lable表示下载任务一直在跟进
    string lable("|/-\\"); 
    //注意,这里要保留之前的进度,需设置静态
    static char buffer[Max] = { 0 };
    static int cnt = 0;
    if (rate <= 1.0)
    {
        buffer[0] = Head;
    }
    printf("\033[1;31;46m[%-100s]\033[0m[%.1lf%%][%c]\r", buffer, rate, lable[cnt % lable.size()]);  //设置色彩,这里我们设置高亮/加粗,青色背景,红色字体的色彩
    fflush(stdout);
    //下面控制进度的跟进
    buffer[(int)rate] = Body;
    if ((int)rate + 1 < 100)
    {
        buffer[(int)(rate + 1)] = Head;
    }
    if (rate >= 100.0)
    {
        cout << endl;
    }
    cnt++;
    cnt %= lable.size();
}


总代码如下:


#include "process.h"
//版本一
void process1()
{
    string lable("|/-\\");
    string nums;
    int count = 0;
    int lablesize = lable.size();
    nums.push_back(Head);
    while (count <= 100)
    {
        cout << "[" << left << setw(100) << nums << "]";
        cout << "[" << "%" << count << "]";
        cout << "[" << lable[count % lablesize] << "]" << '\r';
        fflush(stdout);
        nums.clear();
        count++;
        nums.append(count, Body);
        if (count < 100)
        {
            nums.push_back(Head);
        }
        usleep(60000);
    }
    cout << endl;
}
//版本二
void download(callback_t cb)
{
    srand(time(0) * 1024);
    int total = FileSize;
    while (total)
    {
        usleep(10000);
        int one = rand() % (1024 * 1024 * 5);
        total -= one;
        if (total < 0)
        {
            total = 0;
        }
        int download = FileSize - total;
        double rate = (download * 1.0 / (FileSize)) * 100.0;
        cb(rate);
    }
}
void process2(double rate)
{
    static string lable("|/-\\");
    static char buffer[Max] = { 0 };
    static int cnt = 0;
    if (rate <= 1.0)
    {
        buffer[0] = Head;
    }
    printf("\033[1;31;46m[%-100s]\033[0m[%.1lf%%][%c]\r", buffer, rate, lable[cnt % lable.size()]);
    fflush(stdout);
    buffer[(int)rate] = Body;
    if ((int)rate + 1 < 100)
    {
        buffer[(int)(rate + 1)] = Head;
    }
    if (rate >= 100.0)
    {
        cout << endl;
    }
    cnt++;
    cnt %= lable.size();
}
int main()
{
    //process1(); //使用进度条粗略版本一
    download(process2); //使用进度条进化版本二
    return 0;
}

       最后,要说明的是,以上程序都是在Linux系统下运行进行的,在VS或其它编译器下可能会出现错误消息,这时因为不同平台支持的C标准或系统设置不同而造成的差异。


相关文章
|
7月前
|
Ubuntu Linux Anolis
Linux系统禁用swap
本文介绍了在新版本Linux系统(如Ubuntu 20.04+、CentOS Stream、openEuler等)中禁用swap的两种方法。传统通过注释/etc/fstab中swap行的方式已失效,现需使用systemd管理swap.target服务或在/etc/fstab中添加noauto参数实现禁用。方法1通过屏蔽swap.target适用于新版系统,方法2通过修改fstab挂载选项更通用,兼容所有系统。
613 3
Linux系统禁用swap
|
7月前
|
Linux
Linux系统修改网卡名为eth0、eth1
在Linux系统中,可通过修改GRUB配置和创建Udev规则或使用systemd链接文件,将网卡名改为`eth0`、`eth1`等传统命名方式,适用于多种发行版并支持多网卡配置。
1143 3
|
Ubuntu Linux 网络安全
Linux系统初始化脚本
一款支持Rocky、CentOS、Ubuntu、Debian、openEuler等主流Linux发行版的系统初始化Shell脚本,涵盖网络配置、主机名设置、镜像源更换、安全加固等多项功能,适配单/双网卡环境,支持UEFI引导,提供多版本下载与持续更新。
718 3
Linux系统初始化脚本
|
8月前
|
运维 Linux 开发者
Linux系统中使用Python的ping3库进行网络连通性测试
以上步骤展示了如何利用 Python 的 `ping3` 库来检测网络连通性,并且提供了基本错误处理方法以确保程序能够优雅地处理各种意外情形。通过简洁明快、易读易懂、实操性强等特点使得该方法非常适合开发者或系统管理员快速集成至自动化工具链之内进行日常运维任务之需求满足。
544 18
|
7月前
|
安全 Linux Shell
Linux系统提权方式全面总结:从基础到高级攻防技术
本文全面总结Linux系统提权技术,涵盖权限体系、配置错误、漏洞利用、密码攻击等方法,帮助安全研究人员掌握攻防技术,提升系统防护能力。
822 1
|
7月前
|
监控 安全 Linux
Linux系统提权之计划任务(Cron Jobs)提权
在Linux系统中,计划任务(Cron Jobs)常用于定时执行脚本或命令。若配置不当,攻击者可利用其提权至root权限。常见漏洞包括可写的Cron脚本、目录、通配符注入及PATH变量劫持。攻击者通过修改脚本、创建恶意任务或注入命令实现提权。系统管理员应遵循最小权限原则、使用绝对路径、避免通配符、设置安全PATH并定期审计,以防范此类攻击。
1239 1
|
8月前
|
缓存 监控 Linux
Linux系统清理缓存(buff/cache)的有效方法。
总结而言,在大多数情形下你不必担心Linux中buffer与cache占用过多内存在影响到其他程序运行;因为当程序请求更多内存在没有足够可用资源时,Linux会自行调整其占有量。只有当你明确知道当前环境与需求并希望立即回收这部分资源给即将运行重负载任务之前才考虑上述方法去主动干预。
2146 10
|
8月前
|
安全 Linux 数据安全/隐私保护
为Linux系统的普通账户授予sudo访问权限的过程
完成上述步骤后,你提升的用户就能够使用 `sudo`命令来执行管理员级别的操作,而无需切换到root用户。这是一种更加安全和便捷的权限管理方式,因为它能够留下完整的权限使用记录,并以最小权限的方式工作。需要注意的是,随意授予sudo权限可能会使系统暴露在风险之中,尤其是在用户不了解其所执行命令可能带来的后果的情况下。所以在配置sudo权限时,必须谨慎行事。
1404 0
|
8月前
|
Ubuntu Linux 开发者
国产 Linux 发行版再添新成员,CutefishOS 系统简单体验
当然,系统生态构建过程并不简单,不过为了帮助国产操作系统优化生态圈,部分企业也开始用国产操作系统替代 Windows,我们相信肯定会有越来越多的精品软件登录 Linux 平台。
621 0
|
8月前
|
Ubuntu 安全 Linux
Linux系统入门指南:从零开始学习Linux
Shell脚本是一种强大的自动化工具,可以帮助您简化重复的任务或创建复杂的脚本程序。了解Shell脚本的基本语法和常用命令,以及编写和运行Shell脚本的步骤,将使您更高效地处理日常任务。
694 0