引言

命令行程序在处理耗时任务时,一个直观的进度条能极大提升用户体验。然而 C++ 标准库并未提供终端进度条组件,开发者要么引入重型第三方库,要么自己手写 ANSI 转义码控制光标。

progress_barutilsxx 库中的轻量级终端进度条组件,它零依赖、跨平台、开箱即用,同时提供了 indicators 第三方库的集成示例,满足不同场景需求。本文将带你一文看懂如何使用它,以及有哪些实用技巧。


一、快速上手:三行代码显示进度条

#include "progress_bar.hpp"

int main() {
    utilsxx::progress_bar bar("Processing", 101);
    
    for (int i = 0; i < 101; ++i) {
        bar.tick();  // 每步调用一次
        // 你的耗时操作...
    }
    
    return 0;
}

终端输出效果:

Processing |████████████████████████████████████████░░░░░░░░░░|  75.0%

二、核心设计

2.1 两种显示模式

模式枚举值说明
完整模式Full进度条 + 百分比数字(默认)
数字模式NumberOnly仅显示百分比数字,无图形条

2.2 颜色系统

支持已完成部分和未完成部分分别设置颜色:

颜色枚举值ANSI 效果
白色White默认粗体
绿色Green粗体绿色
红色Red粗体红色
蓝色Blue粗体蓝色
黄色Yellow粗体黄色

2.3 智能宽度适配

进度条长度自动根据终端宽度计算,无需手动调整:

bar_length = (terminal_width - description_width - 5) / 2

这意味着无论终端窗口大小如何变化,进度条都能自动适配,不会溢出或留白过多。


三、基础用法详解

3.1 构造函数与 reset()

// 方式1:构造时初始化
utilsxx::progress_bar bar("Task-1", 101, utilsxx::Full, std::clog);

// 方式2:默认构造后 reset
utilsxx::progress_bar bar;
bar.reset("Task-2", 201, utilsxx::Full, std::clog);

参数说明:

参数类型默认值说明
descriptionstring""进度条名称前缀
nunsigned long101总步数(循环次数)
modeprogbar_mode_eFull显示模式
outostream&std::clog输出流,默认标准错误输出

为何默认输出到 std::clog 避免进度条干扰程序的标准输出重定向(如 program > result.txt),进度信息仍显示在终端。

3.2 自定义样式

bar.set_style(
    ">",           // 已完成符号
    "-",           // 未完成符号
    utilsxx::Green,   // 已完成颜色
    utilsxx::White    // 未完成颜色
);

效果:

Sample-2 |>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>----------------------|  50.0%

3.3 完整示例

#include "progress_bar.hpp"
#include <thread>
#include <chrono>

int main() {
    utilsxx::progress_bar bar;
    bar.hide_cursor();  // 隐藏光标,避免闪烁

    // 第一段任务:默认样式
    for (int i = 0; i < 101; ++i) {
        bar.tick();
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }

    // 第二段任务:自定义样式
    bar.reset("Sample-2", 201);
    bar.set_style(">", "-", utilsxx::Green, utilsxx::White);
    
    for (int i = 0; i < 201; ++i) {
        bar.tick();
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }

    bar.show_cursor();  // 恢复光标显示
    return 0;
}

四、高级技巧

4.1 处理未知总步数的任务:动态三点进度条

有些任务(如下载、网络请求)无法预知总步数,此时可用 dots() 方法显示动态三点动画:

bar.reset("Downloading data from Internet");

for (int i = 0; i < 20; ++i) {
    bar.dots();  // 显示 "Downloading..." -> "Downloading.." -> "Downloading." 循环
    std::this_thread::sleep_for(std::chrono::milliseconds(500));
}

bar.done();  // 输出 "Downloading data from Internet... done!"

dots() 的内部机制:

  • 固定每 500 毫秒 更新一次显示,不受调用频率影响
  • 点的数量在 1-3 之间循环,用空格清除残留字符
  • 适合配合 execute_with_progress() 用于异步任务

4.2 异步任务封装:execute_with_progress()

对于真正的异步任务(如文件下载、数据库查询),使用 execute_with_progress() 模板函数:

bar.reset("Downloading data from Internet");

utilsxx::execute_with_progress(bar, []() {
    // 你的耗时任务...
    std::this_thread::sleep_for(std::chrono::seconds(5));
});

原理:

  1. 自动隐藏光标
  2. 在工作线程中执行你的函数
  3. 主线程循环调用 dots() 显示进度动画
  4. 任务完成后自动调用 done() 并恢复光标

可自定义检查间隔(默认 250ms):

// 对于长时间任务,减少刷新频率以降低开销
utilsxx::execute_with_progress(bar, long_task, 1000);  // 每秒检查一次

4.3 光标控制

bar.hide_cursor();  // 隐藏光标,进度条更新时不会闪烁
// ... 任务执行中 ...
bar.show_cursor();  // 恢复光标显示

注意hide_cursor()show_cursor() 使用 ANSI 转义码 \033[?25l / \033[?25h,在析构函数中会自动恢复光标,防止程序异常退出后光标消失。

4.4 多阶段任务管理

利用 reset() 可以在同一个进度条对象上执行多阶段任务:

utilsxx::progress_bar bar;
bar.hide_cursor();

// 阶段1:数据加载
bar.reset("Loading data", 100);
for (int i = 0; i < 100; ++i) { bar.tick(); /* load */ }

// 阶段2:数据处理
bar.reset("Processing", 500);
bar.set_style("#", " ");
for (int i = 0; i < 500; ++i) { bar.tick(); /* process */ }

// 阶段3:结果保存
bar.reset("Saving results", 50);
for (int i = 0; i < 50; ++i) { bar.tick(); /* save */ }

bar.show_cursor();

五、indicators 库:更丰富的进度条生态

utilsxx 同时集成了 indicators 库(位于 lib/indicators.hpp),提供更丰富的进度条样式:

5.1 Unicode 进度条

#include "indicators.hpp"

indicators::ProgressBar bar{
    indicators::option::BarWidth{50},
    indicators::option::Start{"["},
    indicators::option::Fill{"🔥"},
    indicators::option::Lead{"🔥"},
    indicators::option::Remainder{" "},
    indicators::option::End{" ]"},
    indicators::option::PostfixText{"Emojis"},
    indicators::option::ForegroundColor{indicators::Color::white},
    indicators::option::FontStyles{
        std::vector<indicators::FontStyle>{indicators::FontStyle::bold}}
};

while (true) {
    bar.tick();
    if (bar.is_completed()) break;
}

5.2 块进度条

indicators::BlockProgressBar bar{
    indicators::option::BarWidth{80},
    indicators::option::FontStyles{
        std::vector<indicators::FontStyle>{indicators::FontStyle::bold}}
};

size_t progress = 0;
while (true) {
    bar.set_progress(progress++);
    if (bar.is_completed()) break;
}

5.3 选择建议

场景推荐方案
简单任务,快速集成utilsxx::progress_bar
需要 Unicode/Emoji 支持indicators::ProgressBar
需要不确定进度动画utilsxx::progress_bar::dots()
需要多进度条并发indicators 的多进度条支持
极简依赖,单头文件utilsxx::progress_bar

六、API 速查表

progress_bar 类

方法说明
progress_bar(desc, n, mode, out)构造函数
reset(desc, n, mode, out)重置进度条状态
tick()前进一步,更新显示
dots()显示动态三点动画
done()输出 “… done!”
set_style(bar, space, done_color, todo_color)自定义符号和颜色
hide_cursor()隐藏终端光标
show_cursor()显示终端光标

辅助函数

函数说明
execute_with_progress(bar, func, interval)异步执行函数并显示三点进度

枚举类型

枚举
progbar_mode_eFull, NumberOnly
progbar_color_eWhite, Green, Red, Blue, Yellow

七、完整示例

#include "progress_bar.hpp"
#include <thread>
#include <chrono>
#include <vector>

// 模拟一个多阶段数据处理任务
void process_dataset() {
    utilsxx::progress_bar bar;
    bar.hide_cursor();

    // 阶段1:读取文件(100个文件)
    bar.reset("Reading files", 100);
    for (int i = 0; i < 100; ++i) {
        bar.tick();
        std::this_thread::sleep_for(std::chrono::milliseconds(5));
    }

    // 阶段2:计算(500步,绿色进度条)
    bar.reset("Computing", 500);
    bar.set_style(">", "-", utilsxx::Green, utilsxx::White);
    for (int i = 0; i < 500; ++i) {
        bar.tick();
        std::this_thread::sleep_for(std::chrono::milliseconds(2));
    }

    // 阶段3:保存结果(未知步数,使用异步进度)
    bar.reset("Saving to database");
    utilsxx::execute_with_progress(bar, []() {
        std::this_thread::sleep_for(std::chrono::seconds(3));
    }, 500);

    bar.show_cursor();
}

int main() {
    process_dataset();
    return 0;
}

结语

progress_bar 以极简的 API 提供了完整的终端进度条功能:自动宽度适配、颜色控制、多阶段复用、异步任务支持。对于需要更花哨效果的项目,utilsxx 也集成了 indicators 库作为补充。

无论是科学计算、数据处理还是文件转换,给耗时操作加上一个进度条,都是提升用户体验最简单有效的方式。


项目地址utilsxx