如何利用ChatGPT快速实现一个控制台进度条小工具?

本篇讲解如何利用 chatgpt 快速实现一个控制台进度条小工具,相比单纯介绍某些特性,此种方式涉及知识的综合运用,也顺便谈谈如何结合 ai 进行编程。
问题描述
控制台程序执行一些耗时任务时,需要向用户显示当前任务执行的进度,以提供清晰的感知。比如一个下载程序,通过进度条便能告知用户当前的下载进度。
进度条可以单独显示,也可以在程序输出的最下方显示,下图是一个示例。
这是一种单控制条需求,执行任务,显示进度,输出流依旧是从上至下依序进行,适合单线程的场景。
多控制条显示的效果如下图,实现要更加复杂一些,本文暂时不会涉及该部分。
初步分析
控制台上显示的这种符号,称为 ascii art,就是以字符构建的某种图案,不借助图片,也能够有一个生动的展示效果,比如下图这种。
因此控制台进度条也称为 ascii progress bar,通过字符图案来模拟进度条的显示,通常分为已完成部分及未完成部分,使用两个字符,动态改变字符数量,便能够模拟出一个进度条。
模拟方式既定,下一问题在于进度条刷新。如果每更新一次进度,便输出一个字符图案,那么屏幕上将满是进度条,需要针对一条进度条,不断刷新其数据,而非每次都输出一条新的。具体实现时,便需要寻找定位进度条的方法,每次清除当前数据,重新打印新的数据,视觉上显示的是连续动画。
刷新思路亦成,接着的问题在于如何在进度条之上插入其他输出。进度条始终显示在用户输出下方,因此每次用户输出时,可以立即定位到进度条,定位之后清除当前进度条,输出用户内容,再重新打印进度条,便能够达到这一效果。
细枝末节,便需依赖具体的实现手法。
借助 chatgpt 快速构建基本代码
需求明确,思路既定,接着便要着手设计库的结构和细节,实现细节这部分代码无需从零编写,可以借助 ai 快速生成。
我们所需做的,就是详细描述需求,以及预想的思路,让 chatgpt 生成代码,验证是否符合需求,若不符合,纠正错误,让它再次生成,不断重复这个过程,直到基本满足期待的效果。如果一开始的效果就完全牛头不对马嘴,那也可以让它基于 python 生成,等到效果尚可,再让它把代码转换成 c++ 代码。
经过多次调教,最终生成的代码如下:
1#include  2#include  3#include  4 5void print_progress_bar(int iteration, int total, int bar_length = 50) { 6    float progress = static_cast(iteration) / total; 7    int arrow_length = static_cast(bar_length * progress); 8    int spaces = bar_length - arrow_length; 910    std::cout << progress: [;11    for (int i = 0; i < arrow_length; ++i) {12        std::cout << -;13    }14    for (int i = 0; i < spaces; ++i) {15        std::cout <<  ;16    }17    std::cout << ]  << progress * 100 << % << std::flush;18}1920int main() {21    int total_iterations = 100;22    for (int i = 0; i <= total_iterations; ++i) {23        // 输出其他内容24        std::cout << �33[2ksome other output  << i << std::endl;2526        // 更新并输出进度条27        print_progress_bar(i, total_iterations);28        std::cout << std::flush;2930        // 模拟任务执行时间31        std::sleep_for(std::milliseconds(100));32    }3334    // 输出一个换行来防止进度条下一行被覆盖35    std::cout < void {15        m_bar_length = len;16    }1718    auto bar_length() const -> int {19        return m_bar_length;20    }2122    // ...2324private:25    int m_bar_length;       // 控制条长度26    int m_data_length;      // 数据长度27    char m_done_char;       // 已完成字符28    char m_undone_char;     // 未完成字符29    char m_opening_bracket; // 开括号30    char m_closing_bracket; // 闭括号31    std::ostream& m_os;     // 输出流32    std::string m_desc;     // 控制条描述信息33};  
其次,将「清除并回到行首」和「输出控制条」这两部分抽离出来,它们一个在用户内容之前输出,一个在之后输出,于是增加一个 before() 和 after() 接口来表示。
1auto progress_bar::before() const -> void { 2    m_os < void { 6    auto progress = static_cast(cur) / m_data_length; 7    auto finished_length = static_cast(progress * m_bar_length); 8 9    m_os << m_desc << :  << m_opening_bracket;10    for (int i = 0; i < finished_length; ++i) {11        m_os << m_done_char;12    }13    for (int i = 0; i < m_bar_length - finished_length; ++i) {14        m_os << m_undone_char;15    }16    m_os << m_closing_bracket << progress * 100 << % < void { 2    this->before(); 3    this->after(cur); 4} 5 6auto progress_bar::update(int cur, std::invocable auto fn) const -> void { 7    this->before(); 8    std::invoke(fn, cur); 9    this->after(cur);10}  
提供两个重载版本,以应对无用户输出时的变化性。
现在,便可以这样使用:
1int main() { 2    int total = 50; 3    cpb::progress_bar progress(total); 4    for (auto i : std::iota(0, total)) { 5        // update the progress bar 6        progress.update(i + 1, [](int v) { 7            std::cout << some other output  << v < void {2    auto progress = static_cast(cur) / m_data_length;3    auto finished_length = static_cast(progress * m_bar_length);45    auto progress_info = std::format(progress: [{3:>3}%] [{0:#^{1}}{0:.^{2}}], , finish_length, bar_length - finish_length, static_cast(progress * 100));6    m_os << progress_info << std::flush;7}  
通过优化,核心代码只剩下一行,这就是 fmt 库的强大所在。
但是,格式化时没有动态指定填充字符,这是因为 fmt 暂时不支持这种代码:
1   // =====2   std::cout << std::format({:=^{}}, , 5); // ok3   std::cout < void {2    auto progress = static_cast(cur) / m_data_length;3    auto finished_length = static_cast(progress * m_bar_length);45    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);6    m_os << progress_info < void {2    aopcxx::make_aspect(this, cur);3}45auto update(int cur, std::invocable auto fn) const -> void {6    aopcxx::make_aspect(this, fn, cur);7}  
原理几年前便已讲过,扩展的源码可以自行去看。
示例
至此,三两下库已成型,可以这样使用:
1int main() { 2    int total = 50; 3    cpb::progress_bar progress(total); 4    for (auto i : std::iota(0, total)) { 5        // update the progress bar 6        progress.update(i + 1, [](int v) { 7            std::cout << some other output  << v << ''; 8        }); 9        std::sleep_for(std::milliseconds(100));10    }1112    std::cout << ;13    progress.done_char('x');14    progress.undone_char('-');15    for (auto b : std::iota(0, total)) {16        progress.update(b + 1);17        std::sleep_for(std::milliseconds(100));18    }19}  
如果想亲自测试一下代码或查看源码,可以通过以下指令:
1git clone https://github.com/lkimuk/cpp-progress-bar.git2mkdir build && cd build3cmake ..4make5./test  
总结
思路想法确定,借助 chatgpt 快速生成代码雏形,能够加快开发速度,让你避开细枝末节,快速让目标运行起来。
在此基础上,自己只需专注代码优化,将精力放在核心功能上。
尽管后期可能会替换掉 ai 生成的所有实现,但也要事半功倍。先快速让程序跑起来,再去优化局部细节,比完全从局部细节构建起整体结构,要高效得多。
大家可以尝试使用起来。


支持全球模拟和数字电视标准的硅调谐器
在AI技术加持下,能够一眼认出潦草的简笔画
将PWM信号转为负电压的电路图解析
云从科技从搭建轻舟生态入手,破解行业应用难题
百般忍耐,iphone7亮黑还是难逃进客服的命!
如何利用ChatGPT快速实现一个控制台进度条小工具?
CAD图纸管理方法,CAD图纸管理软件
台积电砸30亿美元扩产能 迎接苹果订单
dfrobot切诺基4WD智能机器人开发平台介绍
晶电开发出能让混光区域趋近于零的芯片
为您精选上周10则您不能错过的新闻,京东方OLED抢苹单传言再起
关于AH173霍尔传感器的相关应用介绍
三分钟能做什么?三分钟能让我的iPhone6s重启N次!
浅谈CAN总线技术在汽车ECU中的开发
MyBatis Plus解决大数据量查询慢问题
“开关电源”和“普通电源”的区别是什么
2013年全球PC出货量大减6.9%,连续七个季度下滑
AT89S51单片机驱动扬声器实现报警器功能的设计
美采购外国军备引国内不满 污蔑中国芯片劣质
OTDR光缆线路障碍判断