面包屑图标 当前位置: 首页
AI资讯
热点详情

用ChatGPT快速实现控制台进度条小工具

AI热点日报
AI热点日报时间:2026-05-29
热点解读

利用ChatGPT辅助编程,实现控制台进度条工具。通过ASCII字符模拟进度,采用刷新方式动态显示,支持设置宽度与填充样式。详细描述需求与思路后,由AI生成并迭代修正代码,最终得到简洁高效的C++实现。

# 用ChatGPT快速搭建一个控制台进度条工具 本篇讲解如何利用ChatGPT快速实现一个控制台进度条小工具。相比单纯介绍某些特性,这种方式涉及知识的综合运用,也顺便谈谈如何结合AI进行编程。 ## 问题描述 控制台程序执行一些耗时任务时,总得给用户一点反馈——当前任务执行到哪一步了,进度如何。比如一个下载程序,通过进度条便能告知用户当前的下载进度。 进度条可以单独显示,也可以在程序输出的最下方显示,下图是一个示例。 这是一种单控制条需求:执行任务,显示进度,输出流依旧是从上至下依序进行,适合单线程的场景。 多控制条显示的效果如下图,实现要更加复杂一些,本文暂时不会涉及该部分。 ## 初步分析 控制台上显示的这种符号,称为ASCII Art,就是以字符构建的某种图案,不借助图片,也能够有一个生动的展示效果,比如下图这种。 因此控制台进度条也称为ASCII Progress Bar,通过字符图案来模拟进度条的显示。通常分为已完成部分及未完成部分,使用两个字符,动态改变字符数量,便能够模拟出一个进度条。 模拟方式既定,下一问题在于进度条刷新。如果每更新一次进度,便输出一个字符图案,那么屏幕上将满是进度条。需要针对一条进度条,不断刷新其数据,而非每次都输出一条新的。具体实现时,便需要寻找定位进度条的方法,每次清除当前数据,重新打印新的数据,视觉上显示的是连续动画。 刷新思路亦成,接着的问题在于如何在进度条之上插入其他输出。进度条始终显示在用户输出下方,因此每次用户输出时,可以立即定位到进度条,定位之后清除当前进度条,输出用户内容,再重新打印进度条,便能够达到这一效果。 细枝末节,便需依赖具体的实现手法。 ## 借助ChatGPT快速构建基本代码 需求明确,思路既定,接着便要着手设计库的结构和细节。实现细节这部分代码无需从零编写,可以借助AI快速生成。 我们所需做的,就是详细描述需求,以及预想的思路,让ChatGPT生成代码,验证是否符合需求。若不符合,纠正错误,让它再次生成,不断重复这个过程,直到基本满足期待的效果。如果一开始的效果就完全牛头不对马嘴,那也可以让它基于Python生成,等到效果尚可,再让它把代码转换成C++代码。 经过多次调教,最终生成的代码如下: ```cpp #include #include #include void print_progress_bar(int iteration, int total, int bar_length = 50) { float progress = static_cast(iteration) / total; int arrow_length = static_cast(bar_length * progress); int spaces = bar_length - arrow_length; std::cout << "Progress: ["; for (int i = 0; i < arrow_length; ++i) { std::cout << "-"; } for (int i = 0; i < spaces; ++i) { std::cout << " "; } std::cout << "] " << progress * 100 << "%" << std::flush; } int main() { int total_iterations = 100; for (int i = 0; i <= total_iterations; ++i) { // 输出其他内容 std::cout << "\33[2KSome other output " << i << std::endl; // 更新并输出进度条 print_progress_bar(i, total_iterations); std::cout << std::flush; // 模拟任务执行时间 std::this_thread::sleep_for(std::chrono::milliseconds(100)); } // 输出一个换行来防止进度条下一行被覆盖 std::cout << std::endl; return 0; } ``` 生成的最终代码基本满足需求,能够按预期显示进度条。 对于进度条,生成的代码使用`-`表示完成部分,空格表示未完成部分,每隔100ms刷新一次进度数据。进度条长度固定为50,依据当前进度和控制条长度,动态计算已完成和未完成字符长度,通过循环打印出来。 而刷新显示,这也是调教时最麻烦的一个——起初生成的代码一直会不断打印控制条,最终告诉它使用ANSI escape codes才纠正为`\33[2K`。 这个转义码可以分为两部分:`\33[2K`和`\r`。后者比较常见,就是将光标移动到当前行的开头;而前者的作用是清除当前光标所在行,其实包含三个参数,意义分别为: - `0K`:清除从光标所在位置到行尾的内容; - `1K`:清除从行首到光标所在位置的内容; - `2K`:清除整行内容。 组合起来,作用是每次输出用户内容(用户内容最后需要换行,否则最后一行内容可能会被清除)时,先清除最后一行内容,再将光标移至行首,达到的效果就是清除当前进度条并回到行首。如果仅仅将光标移至行首,而不清除当前行,后面打印的内容若是比原有内容(进度条)长度短,便会留下原有内容的残余部分,致界面混乱。 用户的最后一行输出是换行符,生成代码利用`\r`将光标移至行首,再输出进度条,其实完全多余——光标本来就在行首,每次打印的进度条本身就在用户内容的下方。 ## 封装成类,微调代码 ChatGPT生成的代码,虽然能够达到效果,但最多只有六十分,只是快速实现细节,省些力气而已,仍需要进一步微调。 构建一个cpp-progress-bar项目,以Header-only的形式添加一个progress_bar类,将变化点全部封装起来。 首先,将所有可定制的数据全部抽离出来,比如控制条长度、数据长度、已完成字符和未完成字符等等。 ```cpp class progress_bar { public: progress_bar(int total, int bar_length = 50, std::ostream& os = std::cout) : m_bar_length{ bar_length } , m_data_length{ total } , m_done_char{ '#' } , m_undone_char{ '.' } , m_opening_bracket{ '[' } , m_closing_bracket{ ']' } , m_os{ os } , m_desc{ "Progress" } {} auto bar_length(int len) -> void { m_bar_length = len; } auto bar_length() const -> int { return m_bar_length; } // ... private: int m_bar_length; // 控制条长度 int m_data_length; // 数据长度 char m_done_char; // 已完成字符 char m_undone_char; // 未完成字符 char m_opening_bracket; // 开括号 char m_closing_bracket; // 闭括号 std::ostream& m_os; // 输出流 std::string m_desc; // 控制条描述信息 }; ``` 其次,将「清除并回到行首」和「输出控制条」这两部分抽离出来——它们一个在用户内容之前输出,一个在之后输出,于是增加一个`before()`和`after()`接口来表示。 ```cpp auto progress_bar::before() const -> void { m_os << "\33[2K"; } auto progress_bar::after(int cur) const -> void { auto progress = static_cast(cur) / m_data_length; auto finished_length = static_cast(progress * m_bar_length); m_os << m_desc << ": " << m_opening_bracket; for (int i = 0; i < finished_length; ++i) { m_os << m_done_char; } for (int i = 0; i < m_bar_length - finished_length; ++i) { m_os << m_undone_char; } m_os << m_closing_bracket << progress * 100 << "%" << std::flush; } ``` 最后,以一个`update()`接口来调用以上这两个接口,更新进度条。 ```cpp auto progress_bar::update(int cur) const -> void { this->before(); this->after(cur); } auto progress_bar::update(int cur, std::invocable auto fn) const -> void { this->before(); std::invoke(fn, cur); this->after(cur); } ``` 提供两个重载版本,以应对无用户输出时的变化性。 现在,便可以这样使用: ```cpp int main() { int total = 50; cpb::progress_bar progress(total); for (auto i : std::views::iota(0, total)) { // Update the progress bar progress.update(i + 1, [](int v) { std::cout << "Some other output " << v << '\n'; }); std::this_thread::sleep_for(std::chrono::milliseconds(100)); } } ``` 这便是一个轻量级库的雏形。 ## 优化实现 雏形已成,但实现方面依旧是ChatGPT的那一套。尽管只有几行代码,生成的代码还是相当丑陋而低效。到这一步,便要开始替换生成的实现。 也就是说,ChatGPT生成的代码仅仅是能跑,雏形交给它来快速生成,后续的封装和优化工作则全由自己来做,替换所有低效实现。 当前留下的生成代码只有生成已完成字符和未完成字符的部分,这部分可以使用C++20 Formatting Library进行优化,简化代码。 ```cpp auto progress_bar::after(int cur) const -> void { auto progress = static_cast(cur) / m_data_length; auto finished_length = static_cast(progress * m_bar_length); auto progress_info = std::format("Progress: [{:>3}%] [{0:#^{1}}{0:.^{2}}]", "", finished_length, m_bar_length - finished_length, static_cast(progress * 100)); m_os << progress_info << std::flush; } ``` 通过优化,核心代码只剩下一行,这就是fmt库的强大所在。 但是,格式化时没有动态指定填充字符,这是因为fmt暂时不支持这种代码: ```cpp // "=====" std::cout << std::format("{:=^{}}", "", 5); // OK std::cout << std::format("{0:{1}^{2}}", "", '-', 5); // Error ``` 由于会产生复杂的开销,默认的`std::formatter`并不支持动态指定填充字符。 可以通过定制来手动实现,代码如下: ```cpp namespace cpb { struct fill { char value; int width; }; } // namespace cpb template <> struct std::formatter { constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); } auto format(const cpb::fill& f, auto& ctx) const { return std::fill_n(ctx.out(), f.width, f.value); } }; ``` 别看仅有几行代码,fmt的定制存在巨坑。 **这种实现是错误的:** ```cpp namespace cpb { struct fill { char value; int width; }; } template <> struct std::formatter { constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); } auto format(const cpb::fill& f, auto& ctx) { return std::fill_n(ctx.out(), f.width, f.value); } }; ``` **这种实现也是错误的:** ```cpp namespace cpb { struct fill { char value; int width; }; } template <> struct std::formatter { auto parse(format_parse_context& ctx) { return ctx.begin(); } auto format(const cpb::fill& f, auto& ctx) const { return std::fill_n(ctx.out(), f.width, f.value); } }; ``` **这种实现还是错误的:** ```cpp namespace cpb { struct fill { char value; int width; }; } template <> struct std::formatter { constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); } auto format(const cpb::fill& f, format_parse_context& ctx) const { return std::fill_n(ctx.out(), f.width, f.value); } }; ``` **这种实现依旧是错误的:** ```cpp struct fill { char value; int width; }; template <> struct std::formatter { constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); } auto format(const fill& f, auto& ctx) const { return std::fill_n(ctx.out(), f.width, f.value); } }; ``` 尤其是最后一个,因为标准存在`std::fill`,此时特化`std::formatter`中使用的`fill`是`std::fill`,而不是自己定义的`fill`,所以必须将其置入命名空间内。 由此也可见,基于特化的这种定制方式非常不友好,出现错误较为莫名其妙。 有了这个定制,便可以使用fill来进一步简化原有实现: ```cpp auto after(int cur) const -> void { auto progress = static_cast(cur) / m_data_length; auto finished_length = static_cast(progress * m_bar_length); auto progress_info = std::format("{}: [{:>3}%] {}{}{}{}", m_desc, static_cast(progress * 100), m_opening_bracket, fill{m_done_char, finished_length}, fill{m_undone_char, m_bar_length - finished_length}, m_closing_bracket); m_os << progress_info << std::flush; } ``` 现在这个实现便精简多了。 ## AOP优化 当前接口存在`before()`和`after()`,这不正是AOP要解决的问题。我们几年前也使用C++20实现过一个轻量级的AOP库,此处可以基此稍微扩展一下,满足当前需求。 于是实现进一步简化为: ```cpp auto update(int cur) const -> void { aopcxx::make_aspect

(this, cur); } auto update(int cur, std::invocable auto fn) const -> void { aopcxx::make_aspect

(this, fn, cur); } ``` 原理几年前便已讲过,扩展的源码可以自行去看。 ## 示例 至此,三两下库已成型,可以这样使用: ```cpp int main() { int total = 50; cpb::progress_bar progress(total); for (auto i : std::views::iota(0, total)) { // Update the progress bar progress.update(i + 1, [](int v) { std::cout << "Some other output " << v << '\n'; }); std::this_thread::sleep_for(std::chrono::milliseconds(100)); } std::cout << ""; progress.done_char('X'); progress.undone_char('-'); for (auto b : std::views::iota(0, total)) { progress.update(b + 1); std::this_thread::sleep_for(std::chrono::milliseconds(100)); } } ``` 如果想亲自测试一下代码或查看源码,可以通过以下指令: ``` git clone https://github.com/lkimuk/cpp-progress-bar.git mkdir build && cd build cmake .. make ./test ``` ## 总结 思路想法确定,借助ChatGPT快速生成代码雏形,能够加快开发速度,让你避开细枝末节,快速让目标运行起来。 在此基础上,自己只需专注代码优化,将精力放在核心功能上。 尽管后期可能会替换掉AI生成的所有实现,但也要事半功倍。先快速让程序跑起来,再去优化局部细节,比完全从局部细节构建起整体结构,要高效得多。 大家可以尝试使用起来。

热点追踪提示词
你是一名 AI 行业编辑,请围绕下面这条热点输出一份资讯解读:
热点:用ChatGPT快速实现控制台进度条小工具要求:
1. 先用一句话解释这条热点在讲什么
2. 再总结它为什么重要
3. 说明会影响哪些 AI 产品或内容方向
4. 最后给出 3 个适合资讯站使用的标题
来源:https://m.elecfans.com/article/2371405.html
ChatGPT

游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。

相关热点
AI热点2026-07-09 15:50
MasterGo AI辅助颜色饱和度快速调整方法

MasterGoAI无法直接调节图片饱和度,因其是UI UX协同设计平台而非图像处理工具。变通方法包括前置在专业软件中调好、利用颜色变量间接控制、叠加图层模拟效果或直接生成新图。未来版本可能加入基础图像增强功能。

AI热点2026-07-09 15:50
石墨AI写会议争议记录提示词如何加入目标人群

明确目标人群身份并嵌入专属关注点关键词,用角色语言重构争议描述,可使石墨AI生成的会议争议记录精准匹配法务、投资人、技术负责人等特定群体的需求,避免信息错配。

AI热点2026-07-09 15:50
百度Comate如何用提示词和规范提升代码质量

通过精准提示词(指定语言版本、嵌入代码锚点、使用禁止句式)和优化IDE配置(关闭行内补全、调整延迟、关闭无关语言补全),可显著提升百度Comate生成代码的质量与规范性。还可利用Comate自动生成代码检查清单作为隐含约束,实现持续质量管控。

AI热点2026-07-09 15:50
文心AI定格动画风格视频创作详细步骤指南

使用文心AI生成定格动画风格视频需先确认引擎权限并切换至4 0turbo模型。关键步骤是构建五要素提示词(主体、动作、场景、风格、参数),通过直连或以图生图方式生成帧序列,再导入剪映设置每帧1秒时长、插入黑场、添加弹跳缩放动画与机械快门声效,以呈现手工顿挫感。

延伸阅读