<acronym id="s8ci2"><small id="s8ci2"></small></acronym>
<rt id="s8ci2"></rt><rt id="s8ci2"><optgroup id="s8ci2"></optgroup></rt>
<acronym id="s8ci2"></acronym>
<acronym id="s8ci2"><center id="s8ci2"></center></acronym>
0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

如何利用ChatGPT快速實現一個控制臺進度條小工具?

CPP開發者 ? 來源:CppMore ? 2024-01-18 13:41 ? 次閱讀

本篇講解如何利用 ChatGPT 快速實現一個控制臺進度條小工具,相比單純介紹某些特性,此種方式涉及知識的綜合運用,也順便談談如何結合 AI 進行編程。

問題描述

控制臺程序執行一些耗時任務時,需要向用戶顯示當前任務執行的進度,以提供清晰的感知。比如一個下載程序,通過進度條便能告知用戶當前的下載進度。

進度條可以單獨顯示,也可以在程序輸出的最下方顯示,下圖是一個示例。

這是一種單控制條需求,執行任務,顯示進度,輸出流依舊是從上至下依序進行,適合單線程的場景。

多控制條顯示的效果如下圖,實現要更加復雜一些,本文暫時不會涉及該部分。

e338dba2-b5b5-11ee-8b88-92fbcf53809c.gif

初步分析

控制臺上顯示的這種符號,稱為 ASCII Art,就是以字符構建的某種圖案,不借助圖片,也能夠有一個生動的展示效果,比如下圖這種。

e36393e2-b5b5-11ee-8b88-92fbcf53809c.png

因此控制臺進度條也稱為 ASCII Progress Bar,通過字符圖案來模擬進度條的顯示,通常分為已完成部分及未完成部分,使用兩個字符,動態改變字符數量,便能夠模擬出一個進度條。

模擬方式既定,下一問題在于進度條刷新。如果每更新一次進度,便輸出一個字符圖案,那么屏幕上將滿是進度條,需要針對一條進度條,不斷刷新其數據,而非每次都輸出一條新的。具體實現時,便需要尋找定位進度條的方法,每次清除當前數據,重新打印新的數據,視覺上顯示的是連續動畫。

刷新思路亦成,接著的問題在于如何在進度條之上插入其他輸出。進度條始終顯示在用戶輸出下方,因此每次用戶輸出時,可以立即定位到進度條,定位之后清除當前進度條,輸出用戶內容,再重新打印進度條,便能夠達到這一效果。

細枝末節,便需依賴具體的實現手法。

借助 ChatGPT 快速構建基本代碼

需求明確,思路既定,接著便要著手設計庫的結構和細節,實現細節這部分代碼無需從零編寫,可以借助 AI 快速生成。

我們所需做的,就是詳細描述需求,以及預想的思路,讓 ChatGPT 生成代碼,驗證是否符合需求,若不符合,糾正錯誤,讓它再次生成,不斷重復這個過程,直到基本滿足期待的效果。如果一開始的效果就完全牛頭不對馬嘴,那也可以讓它基于 Python 生成,等到效果尚可,再讓它把代碼轉換成 C++ 代碼。

經過多次調教,最終生成的代碼如下:

 1#include
 2#include
 3#include
 4
 5voidprint_progress_bar(intiteration,inttotal,intbar_length=50){
 6floatprogress=static_cast(iteration)/total;
 7intarrow_length=static_cast(bar_length*progress);
 8intspaces=bar_length-arrow_length;
 9
10std::cout<

生成的最終代碼基本滿足需求,能夠按預期顯示進度條。

對于進度條,生成的代碼使用 - 表示完成部分, 表示未完成部分,每隔 100ms 刷新一次進度數據。進度條長度固定為 50,依據當前進度和控制條長度,動態計算已完成和未完成字符長度,通過循環打印出來。

而刷新顯示,這也是調教時最麻煩的一個,起初生成的代碼一直會不斷打印控制條,最終告訴它使用 ANSI escape codes 才糾正為 ?33[2K 。

這個轉義碼可以分為兩部分,?33[2K 和 。后者比較常見,就是將光標移動到當前行的開頭,而前者的作用是清除當前光標所在行,其實包含三個參數,意義分別為:

0K:清除從光標所在位置到行尾的內容;

1K:清除從行首到光標所在位置的內容;

2K:清除整行內容。

組合起來,作用是每次輸出用戶內容(用戶內容最后需要換行,否則最后一行內容可能會被清除)時,先清除最后一行內容,再將光標移至行首,達到的效果就是清除當前進度條并回到行首。如果僅僅將光標移至行首,而不清除當前行,后面打印的內容若是比原有內容(進度條)長度短,便會留下原有內容的殘余部分,致界面混亂。

用戶的最后一行輸出是換行符,生成代碼利用 將光標移至行首,再輸出進度條,其實完全多余,光標本來就在行首,每次打印的進度條本身就在用戶內容的下方。

封裝成類,微調代碼

ChatGPT 生成的代碼,雖然能夠達到效果,但最多只有六十分,只是快速實現細節,省些力氣而已,仍需要進一步微調。

構建一個 cpp-progress-bar 項目,以 Header-only 的形式添加一個 progress_bar 類,將變化點全部封裝起來。

首先,將所有可定制的數據,全部抽離出來。比如控制條長度、數據長度、已完成字符和未完成字符等等。

 1classprogress_bar{
 2public:
 3progress_bar(inttotal,intbar_length=50,std::ostream&os=std::cout)
 4:m_bar_length{bar_length}
 5,m_data_length{total}
 6,m_done_char{'#'}
 7,m_undone_char{'.'}
 8,m_opening_bracket{'['}
 9,m_closing_bracket{']'}
10,m_os{os}
11,m_desc{"Progress"}
12{}
13
14autobar_length(intlen)->void{
15m_bar_length=len;
16}
17
18autobar_length()const->int{
19returnm_bar_length;
20}
21
22//...
23
24private:
25intm_bar_length;//控制條長度
26intm_data_length;//數據長度
27charm_done_char;//已完成字符
28charm_undone_char;//未完成字符
29charm_opening_bracket;//開括號
30charm_closing_bracket;//閉括號
31std::ostream&m_os;//輸出流
32std::stringm_desc;//控制條描述信息
33};

其次,將「清除并回到行首」和「輸出控制條」這兩部分抽離出來,它們一個在用戶內容之前輸出,一個在之后輸出,于是增加一個 before() 和 after() 接口來表示。

 1autoprogress_bar::before()const->void{
 2m_os<void{
 6autoprogress=static_cast(cur)/m_data_length;
 7autofinished_length=static_cast(progress*m_bar_length);
 8
 9m_os<

最后,以一個 update() 接口來調用以上這兩個接口,更新進度條。

 1autoprogress_bar::update(intcur)const->void{
 2this->before();
 3this->after(cur);
 4}
 5
 6autoprogress_bar::update(intcur,std::invocableautofn)const->void{
 7this->before();
 8std::invoke(fn,cur);
 9this->after(cur);
10}

提供兩個重載版本,以應對無用戶輸出時的變化性。

現在,便可以這樣使用:

 1intmain(){
 2inttotal=50;
 3cpb::progress_barprogress(total);
 4for(autoi:std::iota(0,total)){
 5//Updatetheprogressbar
 6progress.update(i+1,[](intv){
 7std::cout<

這便是一個輕量級庫的雛形。

優化實現

雛形已成,但實現方面依舊是 ChatGPT 的實現,盡管只有幾行代碼,生成的代碼還是相當丑陋而低效。到這一步,便要開始替換生成的實現。

也就是說,ChatGPT 生成的代碼僅僅是能跑,雛形交給它來快速生成,后續的封裝和優化工作則全由自己來做,替換所有低效實現。

當前留下的生成代碼只有生成已完成字符和未完成字符,這部分可以使用 C++20 Fomatting Library 進行優化,簡化代碼。

1autoprogress_bar::after(intcur)const->void{
2autoprogress=static_cast(cur)/m_data_length;
3autofinished_length=static_cast(progress*m_bar_length);
4
5autoprogress_info=std::format("Progress:[{3:>3}%][{0:#^{1}}{0:.^{2}}]","",finish_length,bar_length-finish_length,static_cast(progress*100));
6m_os<

通過優化,核心代碼只剩下一行,這就是 fmt 庫的強大所在。

但是,格式化時沒有動態指定填充字符,這是因為 fmt 暫時不支持這種代碼:

1//"====="
2std::cout<

由于會產生復雜的開銷,默認的 std::formatter 并不支持動態指定填充字符。

可以通過定制來手動實現,代碼如下:

 1namespacecpb{
 2
 3structfill{
 4charvalue;
 5intwidth;
 6};
 7
 8}//namespacecpb
 9
10
11template<>
12structstd::formatter{
13constexprautoparse(format_parse_context&ctx){returnctx.begin();}
14
15autoformat(constcpb::fill&f,auto&ctx)const{
16returnstd::fill_n(ctx.out(),f.width,f.value);
17}
18};

別看僅有幾行代碼,fmt 的定制存在巨坑。

這種實現是錯誤的:

 1namespacecpb
 2{
 3structfill{
 4charvalue;
 5intwidth;
 6};
 7}
 8
 9template<>
10structstd::formatter{
11constexprautoparse(format_parse_context&ctx){returnctx.begin();}
12
13autoformat(constcpb::fill&f,auto&ctx){
14returnstd::fill_n(ctx.out(),f.width,f.value);
15}
16};

這種實現也是錯誤的:

 1namespacecpb
 2{
 3structfill{
 4charvalue;
 5intwidth;
 6};
 7}
 8
 9template<>
10structstd::formatter{
11autoparse(format_parse_context&ctx){returnctx.begin();}
12
13autoformat(constcpb::fill&f,auto&ctx)const{
14returnstd::fill_n(ctx.out(),f.width,f.value);
15}
16};

這種實現還是錯誤的:

 1namespacecpb
 2{
 3structfill{
 4charvalue;
 5intwidth;
 6};
 7}
 8
 9template<>
10structstd::formatter{
11constexprautoparse(format_parse_context&ctx){returnctx.begin();}
12
13autoformat(constcpb::fill&f,format_parse_context&ctx)const{
14returnstd::fill_n(ctx.out(),f.width,f.value);
15}
16};

這種實現依舊是錯誤的:

 1structfill{
 2charvalue;
 3intwidth;
 4};
 5
 6template<>
 7structstd::formatter{
 8constexprautoparse(format_parse_context&ctx){returnctx.begin();}
 9
10autoformat(constfill&f,auto&ctx)const{
11returnstd::fill_n(ctx.out(),f.width,f.value);
12}
13};

尤其是最后一個,因為標準存在 std::fill,此時特化 std::formatter 中使用的 fill 是 std::fill,而不是自己定義的 fill,所以必須將其置入命名空間內。

由此也可見,基于特化的這種定制方式非常不友好,出現錯誤較為莫名其妙。

有了這個定制,便可以使用 fill 來進一步簡化原有實現:

1autoafter(intcur)const->void{
2autoprogress=static_cast(cur)/m_data_length;
3autofinished_length=static_cast(progress*m_bar_length);
4
5autoprogress_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);
6m_os<

現在這個實現便精簡多了。

AOP 優化

當前接口存在 before() 和 after(),這不正是 AOP 要解決的問題,我們幾年前也使用 C++20 實現過一個輕量級的 AOP 庫,此處可以基此稍微擴展一下,滿足當前需求。

于是實現進一步簡化為:

1autoupdate(intcur)const->void{
2aopcxx::make_aspect(this,cur);
3}
4
5autoupdate(intcur,std::invocableautofn)const->void{
6aopcxx::make_aspect(this,fn,cur);
7}

原理幾年前便已講過,擴展的源碼可以自行去看。

示例

至此,三兩下庫已成型,可以這樣使用:

 1intmain(){
 2inttotal=50;
 3cpb::progress_barprogress(total);
 4for(autoi:std::iota(0,total)){
 5//Updatetheprogressbar
 6progress.update(i+1,[](intv){
 7std::cout<

如果想親自測試一下代碼或查看源碼,可以通過以下指令:

1gitclonehttps://github.com/lkimuk/cpp-progress-bar.git
2mkdirbuild&&cdbuild
3cmake..
4make
5./test

總結

思路想法確定,借助 ChatGPT 快速生成代碼雛形,能夠加快開發速度,讓你避開細枝末節,快速讓目標運行起來。

在此基礎上,自己只需專注代碼優化,將精力放在核心功能上。

盡管后期可能會替換掉 AI 生成的所有實現,但也要事半功倍。先快速讓程序跑起來,再去優化局部細節,比完全從局部細節構建起整體結構,要高效得多。

大家可以嘗試使用起來。





審核編輯:劉清

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • ASCII
    +關注

    關注

    4

    文章

    169

    瀏覽量

    34690
  • C++語言
    +關注

    關注

    0

    文章

    147

    瀏覽量

    6886
  • python
    +關注

    關注

    52

    文章

    4698

    瀏覽量

    83610
  • ChatGPT
    +關注

    關注

    28

    文章

    1481

    瀏覽量

    5506

原文標題:借助 ChatGPT 快速實現一個輕量級的控制臺進度條庫

文章出處:【微信號:CPP開發者,微信公眾號:CPP開發者】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    關于進度條

    我用的labview8.6,初學者,在那能找到進度條??!
    發表于 10-28 11:35

    進度條問題

    如何通過編程的方法改變進度條刻度的最大值?
    發表于 02-20 22:55

    如何顯示vi程序運行的進度條

    程序調用mtalab腳本節點,運行很慢,想設計進度條,實時顯示運行進度,求各位大神幫忙
    發表于 04-01 13:53

    labview的進度條

    這是labview的進度條程序,比較好用
    發表于 08-04 14:30

    關于運用進度條顯示下發過程

    之前搜了下帖子,學會了通過設置時間來創建進度條顯示器,那么進度條可以被運用在下發數據的過程中嗎,比如我要下發
    發表于 05-30 09:54

    第52章 PROGBAR-進度條控件

    52. 4 總結52.1 進度條控件介紹 進度條通常在應用程序中用于實現虛擬化,下面的截圖是設置了皮膚和沒有設置皮膚時的顯示效果: 設置皮膚后顯示效果如下:
    發表于 10-18 09:32

    求教利用Labview和其它語言混合編程如何設計弧形進度條?

    1.利用Labview和其它語言混合編程如何設計弧形進度條?2.求自定義控件Demo
    發表于 03-10 18:25

    labview進度條

    我用labview2017做了文件解壓和復制的vi,解壓過程中不知道真實的解壓進度,怎么才能做一個真實的進度條,要真是的,不是自己規定的
    發表于 04-26 09:10

    labview實現進度條

    進度條
    發表于 03-25 17:06

    怎么設置進度條?

    RT!比如 我創建隨意長度的進度條然后我知道文件的大小 當把這個文件里的數據讀完后進度條
    發表于 08-22 04:35

    C語言注釋刪除小工具是什么

    C語言注釋刪除小工具款刪除c語言注釋并實現編譯的工具,如果你喜歡這款軟件,就快來IT貓撲下載吧!C語言注釋刪除小工具介紹很多編譯器不支持
    發表于 07-14 08:39

    HarmonyOS實戰——ProgressBar進度條組件基本使用

    ProgressBar案例——點擊進度條增加實際進度值需求分析:每單擊進度條組件時,進度條就加 5% 的
    發表于 09-22 23:31

    分享開源的ESP32 物聯網小工具

    描述ESP32 物聯網小工具這個小工具是學習多種技術和技能的機會。這是摘要:了解如何使用 PlatformIO 和 Microsoft Visual Studio Code 對 E
    發表于 06-17 10:03

    自寫小工具

    一個自己寫的小工具感覺還不錯,分享給大家。
    發表于 05-17 09:49 ?35次下載

    電阻分壓計算小工具

    一個計算電阻分壓的小工具
    發表于 09-07 14:54 ?35次下載
    亚洲欧美日韩精品久久_久久精品AⅤ无码中文_日本中文字幕有码在线播放_亚洲视频高清不卡在线观看
    <acronym id="s8ci2"><small id="s8ci2"></small></acronym>
    <rt id="s8ci2"></rt><rt id="s8ci2"><optgroup id="s8ci2"></optgroup></rt>
    <acronym id="s8ci2"></acronym>
    <acronym id="s8ci2"><center id="s8ci2"></center></acronym>