Victor
Victor

Reputation: 14583

Showing progress on a command line C++

I have the following code that runs in a command line application written in C++

std::string filename = m_arguments[1];
unsigned long long size = stoll(m_arguments[2]);
char character = m_arguments[3].c_str()[1]; 

FILE *pFile;
if( (pFile = fopen(filename.c_str(), "wb")) != NULL)
{
  for(int i = 0; i<size; i++)
  {
    fputc(character, pFile);
    unsigned long long  per = 100*i/size;
    if(per % 10 == 0) { cout<<"\r"<<per<<"%"<<flush; }
  }
  fclose(pFile);
}
cout<<endl;

The percentage flickers on the command line. I tried to reduce that flicker with if(per%10 == 0) but nothing changes. What can I do to stop that flickering?

Upvotes: 0

Views: 284

Answers (2)

James Kanze
James Kanze

Reputation: 153909

Just playing around, but with:

int i = 0;
while ( i != 100 ) {
    i += 10;
    std::cout << '\r' << std::setw(2) << i << '%' << std::flush;
    Sleep( 2000 );
}
std::cout << "\rDone" << std::endl;

I couldn't see any flickering.

The problem in your case is that your only test is for per % 10 == 0. If the file is big, you'll end up outputting each value an enormous number of times, after each each byte you transfer. If the file is 100KB (not really very big), this will output for every byte between 0 and 999, then for every byte between 10000 and 10999, and so on.

This shouldn't flicker either, but some trials I did in a standard Windows console window shows that it does. And even if it didn't flicker, it will slow things down enormously. You should be memorizing the last output, however, and only outputting when it changes:

int lastOutput = -1;
for ( ... ) {
    //  ...
    if ( per % 10 == 0 ) {
        int toOutput = 100LL * i / size;
        if ( toOutput != lastOutput ) {
            std::cout << '\r' << toOutput << '%' << std::flush;
            lastOutput = toOutput;
        }
    }
}

This can be done much more efficiently, however. For only ten outputs, I'd probably create a table with ten entries, indicating the values of i for which I want to output, and check against this.

Upvotes: 0

zarzare
zarzare

Reputation: 166

The best thing you can do is to only display the percent if it changed since the last loop. This will minimize the amount of output you do to the console which is also slowing you a lot since its very inefficient. Also fixed the loop variable type.

    std::string filename = m_arguments[1];
    unsigned long long size = stoll(m_arguments[2]);
    char character = m_arguments[3].c_str()[1]; 

    FILE *pFile;
    if( (pFile = fopen(filename.c_str(), "wb")) != NULL)
    {
      int last_per = -1;
      for(unsigned long long i = 0; i<size; i++)
      {
        fputc(character, pFile);
        int  per = (int)(100*i/size);
        if(last_per != per) { cout<<"\r"<<per<<"%"<<flush; last_per = per; }
      }
      fclose(pFile);
    }
    cout<<endl;

Upvotes: 3

Related Questions