98

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?

Ian Campbell
  • 23,484
  • 14
  • 36
  • 57
xmllmx
  • 39,765
  • 26
  • 162
  • 323

12 Answers12

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.

leemes
  • 44,967
  • 21
  • 135
  • 183
65

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

Fred Foo
  • 355,277
  • 75
  • 744
  • 836
James Curran
  • 101,701
  • 37
  • 181
  • 258
  • 39
    + manual flush, otherwise it will not be shown immediately because the output is buffered. – leemes Jan 26 '13 at 18:06
  • And if the user accidentaly hits the enter it breaks down :( Apart from that, it is perhaps the most portable solution, +1. – Ali Jan 26 '13 at 19:00
  • @Ali To avoid that, you would have to disable echoing (see [`man termios`](http://linux.die.net/man/3/termios)) – leemes Jan 26 '13 at 23:27
  • 1
    @leemes `#include `, try that on M$ Windows :) Anyway, thanks for the tip, I will probably try that on Linux. – Ali Jan 27 '13 at 08:47
  • 1
    @Ali There might be an equivalent for W1ndOw$, but I don't know it. ;) – leemes Jan 27 '13 at 14:00
  • Using `"\r\e[0K"` could also help clean the line. (tested under linux) – YuMS Nov 27 '18 at 06:45
65

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% [||||||||||||||||||||||||||||||||||||||||||               ]
razz
  • 9,770
  • 7
  • 50
  • 68
  • Watch out, there should be no `';'` at the end of the first `#define`! – Robb1 May 06 '17 at 09:01
  • 5
    This is the simplest and best solution I have found so far – horro Nov 23 '17 at 08:03
  • Is there a way to display an ascii character instead of | ? Thanks – momo123 Mar 02 '20 at 17:17
  • What is the meaning of the last part, why is there a `""` for the last argument of the `printf()` ? – Sylvain Apr 30 '21 at 13:42
  • 1
    @Sylvain 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. – razz May 01 '21 at 02:37
  • Oh the right side is already "space left-padded". Thanks !! Great solution indeed. – Sylvain May 03 '21 at 09:25
  • 1
    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. – XorOrNor Mar 11 '22 at 11:35
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

John Bandela
  • 2,416
  • 12
  • 19
13

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

Matt Kline
  • 10,149
  • 7
  • 50
  • 87
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.

Flare Cat
  • 591
  • 2
  • 12
  • 24
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;
}
genpfault
  • 51,148
  • 11
  • 85
  • 139
A J
  • 720
  • 1
  • 8
  • 11
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);
}
Felierix
  • 179
  • 3
  • 12
  • You don't use system() and especially the color command. Use `SetConsoleTextAttribute(hConsole, color)` instead and don't forget to reset (color 7). – Felierix Mar 07 '22 at 13:12
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. :)

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" , '#');
}
Akash
  • 139
  • 5
0

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

#include <iostream> //for `cout`
#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
    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`
#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);

    free(lBuffer);

    return 0;
}

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

#include <stdio.h>  //for `printf`
#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
    );

    return 0;
}
WENDYN
  • 650
  • 7
  • 15
0

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));
    }
}
rkuang25
  • 79
  • 7