引言
命令行程序在处理耗时任务时,一个直观的进度条能极大提升用户体验。然而 C++ 标准库并未提供终端进度条组件,开发者要么引入重型第三方库,要么自己手写 ANSI 转义码控制光标。
progress_bar 是 utilsxx 库中的轻量级终端进度条组件,它零依赖、跨平台、开箱即用,同时提供了 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);
参数说明:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
description | string | "" | 进度条名称前缀 |
n | unsigned long | 101 | 总步数(循环次数) |
mode | progbar_mode_e | Full | 显示模式 |
out | ostream& | 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));
});
原理:
- 自动隐藏光标
- 在工作线程中执行你的函数
- 主线程循环调用
dots()显示进度动画 - 任务完成后自动调用
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_e | Full, NumberOnly |
progbar_color_e | White, 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