How to display a progress indicator in pure C/C++ (cout/printf)?
Asked Answered
E

13

101

I'm writing a console program in C++ to download a large file. I know the file size, and I start a work thread to download it. I want to show a progress indicator to make it look cooler.

How can I display different strings at different times, but at the same position, in cout or printf?

Emulation answered 26/1, 2013 at 18:2 Comment(6)
checkout the PDCurses library pdcurses.sourceforge.netConceit
C++ Console Progress Indicator might helpGrishilda
Spawning a wget process is not an option ?Flabellum
curses ... ncursesGaia
possible duplicate of Rewinding std::cout to go back to the beginning of a lineMarkus
Just curious, how are you downloading the file? What libraries/other are you using?Lachrymatory
C
128

With a fixed width of your output, use something like the following:

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 %

Note that this output is shown one line below each other, but in a terminal emulator (I think also in Windows command line) it will be printed on the same line.

At the very end, don't forget to print a newline before printing more stuff.

If you want to remove the bar at the end, you have to overwrite it with spaces, to print something shorter like for example "Done.".

Also, the same can of course be done using printf in C; adapting the code above should be straight-forward.

Carpi answered 26/1, 2013 at 18:11 Comment(0)
I
71

For a C solution with an adjustable progress bar width, you can use the following:

#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);
}

It will output something like this:

 75% [||||||||||||||||||||||||||||||||||||||||||               ]
Inexpungible answered 30/3, 2016 at 17:33 Comment(7)
Watch out, there should be no ';' at the end of the first #define!Paschasia
This is the simplest and best solution I have found so farAlternative
Is there a way to display an ascii character instead of | ? ThanksHideaway
What is the meaning of the last part, why is there a "" for the last argument of the printf() ?Sonometer
@Sonometer The string in the square brackets has two parts; left and right. The left part consists of lpad characters of PBSTR printed using the %.*s specifier, while the right part consists of rpad length of a space left-padded string which we chose to be empty "" so that we only print rpad spaces using the %*s specifier.Inexpungible
Oh the right side is already "space left-padded". Thanks !! Great solution indeed.Sonometer
I think "percentage" parameter name is misleading as the function expects fraction of one ie. 0.1, 0.2, 0.5 and so on, not the percentage ie. 10.0, 20.0, 50.0.Bigod
A
66

You can use a "carriage return" (\r) without a line-feed (\n), and hope your console does the right thing.

Assegai answered 26/1, 2013 at 18:5 Comment(6)
+ manual flush, otherwise it will not be shown immediately because the output is buffered.Carpi
And if the user accidentaly hits the enter it breaks down :( Apart from that, it is perhaps the most portable solution, +1.Pyrophoric
@Pyrophoric To avoid that, you would have to disable echoing (see man termios)Carpi
@Carpi #include <termios.h>, try that on M$ Windows :) Anyway, thanks for the tip, I will probably try that on Linux.Pyrophoric
@Pyrophoric There might be an equivalent for W1ndOw$, but I don't know it. ;)Carpi
Using "\r\e[0K" could also help clean the line. (tested under linux)Ketubim
O
14

You can print a carriage return character (\r) to move the output "cursor" back to the beginning of the current line.

For a more sophisticated approach, take a look at something like ncurses (an API for console text-based interfaces).

Oira answered 26/1, 2013 at 18:5 Comment(2)
+ manual flush, otherwise it will not be shown immediately because the output is buffered.Carpi
+ '\b' for moving the cursor one position left.Spiculum
L
14

Take a look at boost progress_display

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

I think it may do what you need and I believe it is a header only library so nothing to link

Leeuwarden answered 26/1, 2013 at 21:28 Comment(0)
L
7

I know I am a bit late in answering this question, but I made a simple class that does exactly what you want. (keep in mind that I wrote using namespace std; before this.):

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
};

An example on how to use:

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;
}

Note: I made all of the classes' strings public so the bar's appearance can be easily changed.

Lachrymatory answered 20/3, 2016 at 19:42 Comment(0)
C
5

Another way could be showing the "Dots" or any character you want .The below code will print progress indicator [sort of loading...]as dots every after 1 sec.

PS : I am using sleep here. Think twice if performance is concern.

#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;
}
Controversial answered 27/2, 2015 at 14:0 Comment(0)
S
3

Here is a simple one I made:

#include <iostream>
#include <thread>
#include <chrono>
#include <Windows.h>
using namespace std;

int main() {
   // Changing text color (GetStdHandle(-11), colorcode)
   SetConsoleTextAttribute(GetStdHandle(-11), 14);
   
   int barl = 20;
   cout << "[";     
   for (int i = 0; i < barl; i++) {         
      this_thread::sleep_for(chrono::milliseconds(100));
      cout << ":";  
   }
   cout << "]";

   // Reset color
   SetConsoleTextAttribute(GetStdHandle(-11), 7);
}
Superintendency answered 24/12, 2017 at 10:30 Comment(1)
You don't use system() and especially the color command. Use SetConsoleTextAttribute(hConsole, color) instead and don't forget to reset (color 7).Cumshaw
O
1

Simple, you can just use string's fill constructor:

#include <iostream> //for `cout` and flush
#include <string>   //for the constructor
#include <iomanip>  //for `setprecision`

using namespace std;

int main()
{
    const int cTotalLength = 10;
    float lProgress = 0.3;
    cout << 
        "\r[" <<                                            //'\r' aka carriage return should move printer's cursor back at the beginning of the current line
            string(cTotalLength * lProgress, 'X') <<        //printing filled part
            string(cTotalLength * (1 - lProgress), '-') <<  //printing empty part
        "] " <<
        setprecision(3) << 100 * lProgress << "%" <<        //printing percentage
        flush;
    return 0;
}

Which would print:

[XXX-------] 30%

If you need it in pure C

and you would like to be able to customize the size and filler characters at runtime:

#include <stdio.h>  //for `printf` and `fflush`
#include <stdlib.h> //for `malloc`
#include <string.h> //for `memset`

int main()
{
    const int cTotalLength = 10;
    char* lBuffer = malloc((cTotalLength + 1) * sizeof *lBuffer); //array to fit 10 chars + '\0'
    lBuffer[cTotalLength] = '\0'; //terminating it
    
    float lProgress = 0.3;

    int lFilledLength = lProgress * cTotalLength;
    
    memset(lBuffer, 'X', lFilledLength); //filling filled part
    memset(lBuffer + lFilledLength, '-', cTotalLength - lFilledLength); //filling empty part
    printf("\r[%s] %.1f%%", lBuffer, lProgress * 100); //same princip as with the CPP method

    //or you can combine it to a single line if you want to flex ;)
    //printf("\r[%s] %.1f%%", (char*)memset(memset(lBuffer, 'X', lFullLength) + lFullLength, '-', cTotalLength - lFullLength) - lFullLength, lProgress * 100);
    fflush(stdout); //streams are usually buffered and not get printed until you send `\n` or "flush" it

    free(lBuffer);

    return 0;
}

but if you don't need to customize it at runtime:

#include <stdio.h>  //for `printf` and `fflush`
#include <stddef.h> //for `size_t`

int main()
{
    const char cFilled[] = "XXXXXXXXXX";
    const char cEmpty[]  = "----------";
    float lProgress = 0.3;
    
    size_t lFilledStart = (sizeof cFilled - 1) * (1 - lProgress);
    size_t lEmptyStart  = (sizeof cFilled - 1) * lProgress;

    printf("\r[%s%s] %.1f%%",
        cFilled + lFilledStart, //Array of Xs starting at `cTotalLength * (1 - lProgress)` (`cTotalLength * lProgress` characters remaining to EOS)
        cEmpty  + lEmptyStart,  //Array of -s starting at `cTotalLength * lProgress`...
        lProgress * 100 //Percentage
    );
    fflush(stdout);

    return 0;
}
Oarlock answered 1/10, 2022 at 20:40 Comment(0)
T
1

I needed to create a progress bar and some of the answers here would cause the bar to blink or display the percentage short of 100% when done. Here is a version that has no loop other than one that simulates cpu work, it only prints when the next progress unit is incremented.

#include <iostream>
#include <iomanip> // for setw, setprecision, setfill
#include <chrono>
#include <thread> // simulate work on cpu

int main()
{
    int batch_size = 4000;
    int num_bars = 50;
    int batch_per_bar = batch_size / num_bars;

    int progress = 0;

    for (int i = 0; i < batch_size; i++) {
        if (i % batch_per_bar == 0) {    
            std::cout << std::setprecision(3) <<
                      // fill bar with = up to current progress
                      '[' << std::setfill('=') << std::setw(progress) << '>'
                      // fill the rest of the bar with spaces
                      << std::setfill(' ') << std::setw(num_bars - progress + 1)
                      // display bar percentage, \r brings it back to the beginning
                      << ']' << std::setw(3) << ((i + 1) * 100 / batch_size) << '%'
                      << "\r";
            progress++;
        }
        
        // simulate work
        std::this_thread::sleep_for(std::chrono::nanoseconds(1000000));
    }
}
Timoshenko answered 3/12, 2022 at 7:37 Comment(0)
M
1

My very simple C solution:

#include <stdio.h>
#define S_(x) #x
#define S(x) S_(x)
#define PBWIDTH 64
#define PBCHAR '#'
static void progressbar(unsigned percent) {
  char pbstr[PBWIDTH];
  memset(pbstr, PBCHAR, PBWIDTH);
  fprintf(stderr, "\r[%-" S(PBWIDTH) ".*s] %u%%",
      percent * PBWIDTH / 100, pbstr, percent);
}
int main(void) {
  progressbar(70);
  fprintf(stderr, "\n");
}

Outputs:

[############################################                    ] 70%

The memset should be optimized to 1 or 2 vector instructions when compiled for modern CPUs, much smaller than storing the whole string statically and equally fast.

In the programs I use it in, I like to print 0% this way, before the loop that calls progressbar:

fprintf(stderr, "[%-" S(PBWIDTH) ".*s] %u%%", 0, "", 0);

If you prefer the number to be before the bar, change the fprintf in progressbar:

  fprintf(stderr, "\r%3u%% [%-" S(PBWIDTH) ".*s]",
      percent, percent * PBWIDTH / 100, pbstr);

And if you do the optional 0% bit:

fprintf(stderr, "%3u%% [%-" S(PBWIDTH) ".*s]", 0, 0, "");

Modify this all as you see fit. :)

Milquetoast answered 28/6, 2023 at 11:48 Comment(0)
A
0

May be this code will helps you -

#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" , '#');
}
Alfi answered 1/8, 2021 at 17:36 Comment(0)
I
0

I provide an internative method using std::setfill() and std::setw().

#include <iostream>
#include <iomanip>
#include <chrono>
#include <thread>

static void printProgressBar(int percent, int progressWidth=60)
{
    std::cout << "\r[" << std::setfill('#') << std::setw(percent*progressWidth/100) << '#';
    std::cout << std::setfill(' ') << std::setw(progressWidth*(100-percent)/100) << "]";
    std::cout << std::setw(3) << percent << "%";
    std::cout.flush();
}

int main()
{
    using namespace std::chrono_literals;
    
    for (int i=0; i<=100; i+=1)
    {
        printProgressBar(i, 100);
        std::this_thread::sleep_for(20ms);
    }
    
    return 0;
}
Infatuate answered 8/3 at 3:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.