xmllmx
xmllmx

Reputation: 42255

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

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?

Upvotes: 103

Views: 146335

Answers (13)

WENDYN
WENDYN

Reputation: 740

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

Upvotes: 1

king Apple
king Apple

Reputation: 1

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

Upvotes: 0

landfill baby
landfill baby

Reputation: 39

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

Upvotes: 1

rkuang25
rkuang25

Reputation: 121

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

Upvotes: 1

Kugel Blitz
Kugel Blitz

Reputation: 79

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

Upvotes: 3

Akash
Akash

Reputation: 189

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

Upvotes: 0

razz
razz

Reputation: 10110

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% [||||||||||||||||||||||||||||||||||||||||||               ]

Upvotes: 71

A J
A J

Reputation: 780

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

Upvotes: 5

Flare Cat
Flare Cat

Reputation: 601

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.

Upvotes: 7

James Curran
James Curran

Reputation: 103505

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

Upvotes: 68

John Bandela
John Bandela

Reputation: 2436

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

Upvotes: 14

leemes
leemes

Reputation: 45675

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.

Upvotes: 128

Matt Kline
Matt Kline

Reputation: 10487

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

Upvotes: 16

Related Questions