如何在纯 C/C++ (cout/printf) 中显示进度指示器?

Posted

技术标签:

【中文标题】如何在纯 C/C++ (cout/printf) 中显示进度指示器?【英文标题】:How to display a progress indicator in pure C/C++ (cout/printf)? 【发布时间】:2013-01-10 11:35:30 【问题描述】:

我正在用 C++ 编写一个控制台程序来下载一个大文件。我知道文件大小,我启动了一个工作线程来下载它。我想显示一个进度指示器,让它看起来更酷。

如何在 cout 或 printf 中,在不同的时间,但在相同的位置显示不同的字符串?

【问题讨论】:

查看 PDCurses 库 pdcurses.sourceforge.net C++ Console Progress Indicator 可能会有所帮助 不能生成wget 进程? curses ... ncurses Rewinding std::cout to go back to the beginning of a line的可能重复 【参考方案1】:

使用固定宽度的输出,使用如下内容:

float progress = 0.0;
while (progress < 1.0) 
    int barWidth = 70;

    std::cout << "[";
    int pos = barWidth * progress;
    for (int i = 0; i < barWidth; ++i) 
        if (i < pos) std::cout << "=";
        else if (i == pos) std::cout << ">";
        else std::cout << " ";
    
    std::cout << "] " << int(progress * 100.0) << " %\r";
    std::cout.flush();

    progress += 0.16; // for demonstration only

std::cout << std::endl;

http://ideone.com/Yg8NKj

[>                                                                     ] 0 %
[===========>                                                          ] 15 %
[======================>                                               ] 31 %
[=================================>                                    ] 47 %
[============================================>                         ] 63 %
[========================================================>             ] 80 %
[===================================================================>  ] 96 %

请注意,此输出显示在彼此下方一行,但在终端仿真器中(我认为也是在 Windows 命令行中),它​​将在同一行上打印.

最后,不要忘记在打印更多内容之前打印一个换行符。

如果你想删除最后的栏,你必须用空格覆盖它,以打印更短的东西,例如"Done."

同样,当然可以在 C 中使用 printf 来完成相同的操作;修改上面的代码应该是直截了当的。

【讨论】:

【参考方案2】:

您可以使用不带换行符 (\n) 的“回车”(\r),并希望您的控制台做正确的事情。

【讨论】:

+ 手动刷新,否则不会立即显示,因为输出是缓冲的。 如果用户不小心按了回车键,它就会崩溃:(除此之外,它可能是最便携的解决方案,+1。 @Ali 为避免这种情况,您必须禁用回显(请参阅man termios @leemes #include &lt;termios.h&gt;,在 M$ Windows 上试试 :) 无论如何,谢谢你的提示,我可能会在 Linux 上试试。 @Ali W1ndOw$ 可能有等价物,但我不知道。 ;)【参考方案3】:

对于具有可调节进度条宽度的C 解决方案,您可以使用以下内容:

#define PBSTR "||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||"
#define PBWIDTH 60

void printProgress(double percentage) 
    int val = (int) (percentage * 100);
    int lpad = (int) (percentage * PBWIDTH);
    int rpad = PBWIDTH - lpad;
    printf("\r%3d%% [%.*s%*s]", val, lpad, PBSTR, rpad, "");
    fflush(stdout);

它会输出如下内容:

 75% [||||||||||||||||||||||||||||||||||||||||||               ]

【讨论】:

注意,第一个#define的末尾应该没有';' 这是迄今为止我找到的最简单最好的解决方案 有没有办法显示 ascii 字符而不是 | ?谢谢 最后一部分是什么意思,为什么printf()的最后一个参数有"" @Sylvain 方括号中的字符串有两部分;左和右。左边部分由PBSTRlpad 字符组成,使用%.*s 说明符打印,而右边部分由rpad 空格左填充字符串的长度组成,我们选择为空“”,这样我们只使用 %*s 说明符打印 rpad 空格。【参考方案4】:

看看 boost progress_display

http://www.boost.org/doc/libs/1_52_0/libs/timer/doc/original_timer.html#Class%20progress_display

我认为它可以满足您的需求,并且我相信它是一个仅标头库,因此无需链接

【讨论】:

【参考方案5】:

您可以打印回车符 (\r) 将输出“光标”移回当前行的开头。

要了解更复杂的方法,请查看 ncurses(基于控制台文本的界面的 API)之类的东西。

【讨论】:

+ 手动刷新,否则不会立即显示,因为输出是缓冲的。 + '\b' 将光标向左移动一位。 ncurses 为 +1。如果您想做一些更复杂的事情,绝对是要走的路。【参考方案6】:

我知道我回答这个问题有点晚了,但我写了a simple class that does exactly what you want.(请记住,我在此之前写了using namespace std;。):

class pBar 
public:
    void update(double newProgress) 
        currentProgress += newProgress;
        amountOfFiller = (int)((currentProgress / neededProgress)*(double)pBarLength);
    
    void print() 
        currUpdateVal %= pBarUpdater.length();
        cout << "\r" //Bring cursor to start of line
            << firstPartOfpBar; //Print out first part of pBar
        for (int a = 0; a < amountOfFiller; a++)  //Print out current progress
            cout << pBarFiller;
        
        cout << pBarUpdater[currUpdateVal];
        for (int b = 0; b < pBarLength - amountOfFiller; b++)  //Print out spaces
            cout << " ";
        
        cout << lastPartOfpBar //Print out last part of progress bar
            << " (" << (int)(100*(currentProgress/neededProgress)) << "%)" //This just prints out the percent
            << flush;
        currUpdateVal += 1;
    
    std::string firstPartOfpBar = "[", //Change these at will (that is why I made them public)
        lastPartOfpBar = "]",
        pBarFiller = "|",
        pBarUpdater = "/-\\|";
private:
    int amountOfFiller,
        pBarLength = 50, //I would recommend NOT changing this
        currUpdateVal = 0; //Do not change
    double currentProgress = 0, //Do not change
        neededProgress = 100; //I would recommend NOT changing this
;

使用示例:

int main() 
    //Setup:
    pBar bar;
    //Main loop:
    for (int i = 0; i < 100; i++)  //This can be any loop, but I just made this as an example
        //Update pBar:
        bar.update(1); //How much new progress was added (only needed when new progress was added)
        //Print pBar:
        bar.print(); //This should be called more frequently than it is in this demo (you'll have to see what looks best for your program)
        sleep(1);
    
    cout << endl;
    return 0;

注意:我公开了所有类的字符串,以便可以轻松更改栏的外观。

【讨论】:

【参考方案7】:

另一种方式可能是显示“点”或您想要的任何字符。下面的代码将每隔 1 秒将进度指示器 [加载的排序...]打印为点。

PS:我在这里使用睡眠。如果担心性能,请三思。

#include<iostream>
using namespace std;
int main()

    int count = 0;
    cout << "Will load in 10 Sec " << endl << "Loading ";
    for(count;count < 10; ++count)
        cout << ". " ;
        fflush(stdout);
        sleep(1);
    
    cout << endl << "Done" <<endl;
    return 0;

【讨论】:

【参考方案8】:

这是我做的一个简单的:

#include <iostream>
#include <windows.h>

using namespace std;

int barl = 20;

int main() 
   system("color 0e");  
   cout << "[";     
   for (int i = 0; i < barl; i++)          
      Sleep(100);       
      cout << ":";  
   
   cout << "]";

【讨论】:

【参考方案9】:

也许这段代码会对你有所帮助 -

#include <iostream>
#include <string>
#include <thread>
#include <chrono>
#include <cmath>

using namespace std;

void show_progress_bar(int time, const std::string &message, char symbol)

    std::string progress_bar;
    const double progress_level = 1.42;

    std::cout << message << "\n\n";

    for (double percentage = 0; percentage <= 100; percentage += progress_level)
    
        progress_bar.insert(0, 1, symbol);
        std::cout << "\r [" << std::ceil(percentage) << '%' << "] " << progress_bar;
        std::this_thread::sleep_for(std::chrono::milliseconds(time));       
    
    std::cout << "\n\n";


int main()

    show_progress_bar(100, "progress" , '#');

【讨论】:

以上是关于如何在纯 C/C++ (cout/printf) 中显示进度指示器?的主要内容,如果未能解决你的问题,请参考以下文章

在cin / cout和scanf / printf期间在C ++中保存float和double precision

如何在纯文本 XML 中分配角色/权限

如何在纯 php 中创建刷新令牌?

如何在纯javascript中显示和隐藏弹出的onclick?

如何在纯 JavaScript 中切换元素的类?

如何在纯 Playground 文件中获取 CoreML