Як відобразити індикатор прогресу в чистому C / C ++ (cout / printf)?


81

Я пишу консольну програму на C ++ для завантаження великого файлу. Я знаю розмір файлу, і я починаю робочу ланцюжок для завантаження. Я хочу показати індикатор прогресу, щоб він виглядав крутішим.

Як відображати різні рядки в різний час, але в одному і тому ж положенні, у cout або printf?


2
Витягує PDCurses бібліотека pdcurses.sourceforge.net
Лефтеріс


2
Нерест wgetпроцесу - це не варіант?
Олександр К.


Відповіді:


56

Ви можете використовувати "повернення каретки" (\ r) без подачі рядків (\ n), і сподіваєтесь, що ваша консоль робить правильно.


31
+ ручне змивання, інакше воно не відображатиметься відразу, оскільки вихідний результат буферизується.
leemes

І якщо користувач випадково потрапляє на вхід, він виходить з ладу :( Окрім цього, це, мабуть, найбільш портативне рішення, +1.
Алі

@Ali Щоб цього уникнути, вам доведеться відключити відлуння (див. man termios)
leemes

1
@leemes #include <termios.h>, спробуй це на M $ Windows :) У будь-якому разі, дякую за підказку, я, мабуть, спробую це на Linux.
Алі

1
@Ali Може існувати еквівалент для W1ndOw $, але я цього не знаю. ;)
leemes

108

З фіксованою шириною виводу використовуйте щось на зразок наступного:

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

Крім того, те ж саме, звичайно, можна зробити, використовуючи printfC; адаптація коду вище повинна бути прямою.


37

Для 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!
Робб1,

3
Це найпростіше і найкраще рішення, яке я знайшов на сьогодні
жах

Чи є спосіб відобразити символ ascii замість | ? Дякую
momo123 02.03.20


10

Ви можете надрукувати символ повернення каретки ( \r), щоб перемістити вихідний "курсор" назад на початок поточного рядка.

Для більш складного підходу погляньте на щось на зразок ncurses (API для консольних текстових інтерфейсів).


4
+ ручне змивання, інакше воно не відображатиметься відразу, оскільки вихідний результат буферизується.
leemes

3
+ '\b'для переміщення курсора на одну позицію ліворуч.
Олексій Фрунзе

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 << "]";
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.