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


78

我正在用C ++编写一个控制台程序来下载大文件。我知道文件大小,然后开始工作线程下载。我想显示一个进度指示器,使其看起来更酷。

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


2
检出PDCurses库pdcurses.sourceforge.net
Lefteris

2
C ++控制台进度指示器可能会有所帮助
David L.

2
生成wget进程不是一种选择吗?
Alexandre C.


Answers:


54

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


30
+手动刷新,否则由于输出已缓冲,因此不会立即显示。
leemes 2013年

如果用户不期而遇命中进入它打破了:(除此之外,它也许是最便携的解决方案,+1。
阿里

@Ali为了避免这种情况,您必须禁用回显(请参阅man termios
leemes 2013年

1
@leemes #include <termios.h>,在M $ Windows上尝试:)无论如何,感谢您的提示,我可能会在Linux上尝试。
阿里

1
@Ali W1ndOw $可能有一个等效项,但我不知道。;)
leemes 2013年

104

在输出具有固定宽度的情况下,请使用以下内容:

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."

而且,当然可以printf在C中使用相同的方法;修改上面的代码应该简单明了。


35

对于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
Robb1年

1
这是我到目前为止找到的最简单,最好的解决方案
horro

有没有办法显示ascii字符而不是| ?谢谢
momo123


10

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

对于更复杂的方法,请看一下ncurses之类的东西(用于基于控制台文本的界面的API)。


4
+手动刷新,否则由于输出已缓冲,因此不会立即显示。
leemes 2013年

3
+'\b'用于将光标向左移动一个位置。
Alexey Frunze 2013年

1
+1为ncurses。如果您想做任何更复杂的事情,绝对是必经之路。
狮子座

4

我知道回答这个问题有点晚了,但是我做了一个简单的课程,完全可以满足您的要求。(请记住,我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;
}

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


3

另一种方法可能是显示“点”或您想要的任何字符。下面的代码将每隔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;
}

1

这是我做的一个简单的例子:

#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 << "]";
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.