欢迎光临
我们一直在努力

Re:Linux系统篇(十三)特别篇: 实现Linux第⼀个系统程序−进度条

在这里插入图片描述

◆ 博主名称: 晓此方-CSDN博客

大家好,欢迎来到晓此方的博客。

⭐️Linux系列个人专栏:
【主题曲】Linux

⭐️
Re系列专栏:我们思考 (Rethink) · 我们重建 (Rebuild) · 我们记录 (Record)


在这里插入图片描述


文章目录

  • 概要&序論
  • 一、 回车与换行的概念区分
    • 1.1 历史渊源
    • 1.2 现代计算机中的演变
  • 二、 缓冲区问题与刷新机制
    • 2.1 现象观察
    • 2.2 强制刷新:fflush
  • 三、 必备知识储备
    • 3.1 休眠函数:usleep
    • 3.2 进阶技巧:printf 彩色输出
  • 四、 进度条原理与代码实现
    • 4.1 核心设计思路
    • 4.2 版本 1:原理验证版
    • 4.2 升级版:解耦设计
      • 4.2.1设计意图
      • 4.2.2设计步骤
      • 4.2.3代码实现

概要&序論

     Hello 大家好,我是此方。 本篇利用 \\r(回车不换行)控制光标原位刷新,配合 fflush(stdout) 强制冲刷缓冲区,实现一个带百分比和动态旋转光标的 Linux 进度条小程序。 Well——正式开始。

一、 回车与换行的概念区分

     在实现进度条之前,首先要理清两个在底层逻辑中完全不同的概念:回车与换行。

1.1 历史渊源

     这两个概念起源于老式打字机:

  • 回车(\\r):将打印头(或光标)移回到当前行的起始位置,但并不向下移动行。
  • 换行(\\n):将打印头向下移动一行,但并不回到行首。 在这里插入图片描述

1.2 现代计算机中的演变

  • 在 Linux/Unix 系统中,\\n 通常被解释为“回车+换行”,即跳到下一行的开头。
  • 在进度条开发中,我们核心利用的是 \\r(回车)。它能让光标回到当前行的开头,从而通过后续覆盖打印实现动态刷新效果。

二、 缓冲区问题与刷新机制

     在编写 C 语言程序时,输出内容并不会直接显示在屏幕上,而是先进入缓冲区。

2.1 现象观察

     参考图片中的代码示例:

#include <stdio.h>
#include <unistd.h>
int main() {
printf("hello bit!"); // 注意:这里没有加 \\n
sleep(3); // 程序休眠 3 秒
return 0;
}

     现象:程序运行后,屏幕并不会立即打印 hello bit!,而是等待 3 秒后,随着程序的退出才显示出来。      原因:printf 的内容被存在了行缓冲区中。行缓冲区的刷新策略通常是:

  • 遇到 \\n。
  • 缓冲区满。
  • 程序正常退出。
  • 2.2 强制刷新:fflush

         如果我们想在不使用 \\n(因为换行会导致无法在同一行刷新)的情况下立即显示内容,需要手动调用刷新函数:

    fflush(stdout); // 强制刷新标准输出流

    三、 必备知识储备

    3.1 休眠函数:usleep

        在 Linux 系统编程中,除了秒级的 sleep,我们常用更精确的休眠函数来控制进度条的刷新频率。

    • 头文件:#include <unistd.h>
    • 函数原型:int usleep(useconds_t usec);
    • 特点:以**微秒(μs)**为单位(

      1

      =

      1

      ,

      000

      ,

      000

      微秒

      1秒 = 1,000,000微秒

      1=1,000,000微秒)。在进度条实现中,建议设置为 100,000(即 0.1 秒)左右,以获得丝滑的视觉体验。

    3.2 进阶技巧:printf 彩色输出

        为了让进度条更具辨识度,我们可以利用 ANSI 转义码让终端输出带颜色的文本。

    • 基本格式:\\033[属性代码;前景颜色;背景颜色m
    • 常用代码:
      • 字体颜色:31(红)、32(绿)、33(黄)、34(蓝)。
      • 常用属性:0(重置)、1(加粗)、5(闪烁)。
    • 注意事项:设置完颜色后必须使用 \\033[0m 重置属性,否则后续的所有终端输出都会受影响。

    #include <stdio.h>
    int main() {
    // 1. 基础颜色:红色
    printf("\\033[31m这是一行红色的字\\033[0m\\n");
    // 2. 复合属性:绿色 + 加粗 + 黑色背景
    printf("\\033[1;32;40m这是加粗绿字黑底\\033[0m\\n");
    // 3. 进度条常用:黄色闪烁(提示警告)
    printf("\\033[5;33m正在下载,请稍后…\\033[0m\\n");
    return 0;
    }

    四、 进度条原理与代码实现

    4.1 核心设计思路

    一个基本的进度条包含以下要素:

  • 主体展示:使用 [ 和 ] 包裹,中间用 # 或 = 填充。
  • 百分比:实时显示当前进度(如 53%)。
  • 旋转光标:显示程序正在运行(如 | / – \\ 循环)。
  • 原位刷新:利用 \\r 回到行首,配合 fflush 实时刷新。
  • 4.2 版本 1:原理验证版

         下面是根据图片逻辑实现的第一个进度条系统程序:

    #include <stdio.h>
    #include <unistd.h>
    #include <string.h>

    #define SIZE 101
    #define MAX_REVOLVE 4

    int main() {
    char bar[SIZE];
    memset(bar, '\\0', sizeof(bar));
    const char* label = "|/-\\\\"; // 旋转光标
    int i = 0;

    while (i <= 100) {
    // %-100s: 左对齐,预留100个字符空间
    // %%: 打印百分号
    // %c: 打印旋转光标
    printf("[% -100s][%d%%][%c]\\r", bar, i, label[i % MAX_REVOLVE]);
    // [%-100s]:预留100字符宽度并左对齐,确保bar数组增长时外框不闪烁。
    // [%d%%] :打印进度数字,'%%' 转义输出百分号。
    // [%c] :取模循环打印旋转字符(|/-\\),模拟加载动画。
    // \\r :回车符,使光标返回行首,配合后续刷新实现“原位覆盖”动态效果。
    fflush(stdout); // 必须手动刷新,否则看不到动态效果

    bar[i++] = '#';
    usleep(50000); // 微秒单位,50ms 刷新一次
    }
    printf("\\n进度下载完成!\\n");

    return 0;
    }

    4.2 升级版:解耦设计

    在实际工程中,进度条不应“自嗨”,而应由外部任务驱动。本版本通过参数传递,实现了下载逻辑与显示逻辑的分离。

    4.2.1设计意图

    • 解耦:进度条只负责渲染,不关心进度如何产生。
    • 同步:通过外部传入的 current(当前量)和 load(总量)实时计算占比,确保视觉与真实状态同步。

    4.2.2设计步骤

  • 定义接口:progress 函数接收当前进度和总目标。
  • 占比映射:计算 (cur * 100 / load) 得到百分比,并将其作为数组下标。
  • 按需填充:只有当百分比增加时,才向数组中填入 #。
  • 原位刷新:利用 \\r 配合 printf 格式化输出,实现动态刷新。
  • 4.2.3代码实现

    ProgressBar.h

    #pragma once
    #include <iostream>
    #include <windows.h>
    #include <cstring>

    const int NUM = 101;
    const char BAR = '#';

    // 进度条渲染函数声明
    void progress(char* arr, int cur, int load);

    Test.cpp (渲染逻辑)

    #include "ProgressBar.h"

    void progress(char* arr, int cur, int load)
    {
    // 1. 计算当前下载占比 (0-100)
    const int cnt = (cur * 100 / load);

    // 2. 旋转光标:cnt % 4 确保下标在 0-3 之间循环
    const char* pointer = "|-/\\\\";

    // 3. 渲染:%-100s 预留空间,\\r 回到行首覆盖刷新
    printf("[%-100s][%d%%][%c]\\r", arr, cnt, pointer[cnt % 4]);

    // 4. 实时更新:在计算出的占比位置填入进度字符
    if (cnt < 100) arr[cnt] = BAR;
    }

    ProgressBar.cpp (业务逻辑)

    #include "ProgressBar.h"

    int main()
    {
    char arr[NUM];
    memset(arr, 0, sizeof(arr));

    const int DownLoad = 1024; // 模拟需要下载的总量
    int current = 0; // 模拟当前已下载的进度

    while (current <= DownLoad)
    {
    // 将当前下载状态推送到进度条进行渲染
    progress(arr, current, DownLoad);
    current++;
    Sleep(1); // 模拟网络延迟
    }

    printf("\\nDownLoad Complete!\\n");
    return 0;
    }

    在这里插入图片描述


    好的本期内容就到这里,如果对你有帮助,还不要忘记点赞三联支持。我是此方,我们下期再见。bye!

    赞(0)
    未经允许不得转载:171主机测评 » Re:Linux系统篇(十三)特别篇: 实现Linux第⼀个系统程序−进度条
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址