如何在纯 C/C++ (cout/printf) 中显示进度指示器



我正在用C++编写一个控制台程序来下载一个大文件。我知道文件大小,我启动一个工作线程来下载它。我想显示一个进度指示器,使其看起来更酷。

如何在不同时间显示不同的字符串,但在同一位置,在 cout 或 printf 中?

对于输出的固定宽度,请使用如下所示的内容:

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

此外,当然也可以使用 C 中的 printf 来完成同样的事情;调整上面的代码应该是直截了当的。

您可以使用不带换行符 () 的"回车符"(\r),并希望您的控制台做正确的事情。

对于具有可调整进度条宽度的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% [||||||||||||||||||||||||||||||||||||||||||               ]

看看提升progress_display

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

我认为它可以满足您的需求,我相信它是一个仅标题库,因此无需链接

您可以打印回车符 ( r ) 将输出"光标"移回当前行的开头。

对于更复杂的方法,请查看 ncurses(用于控制台基于文本的界面的 API)之类的内容。

我知道

我回答这个问题有点晚了,但我做了一个简单的类,完全可以满足你的要求。(请记住,我在此之前写过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;
}

注意:我公开了所有类的字符串,以便可以轻松更改栏的外观。

另一种方式可能是显示"点"或您想要的任何字符。下面的代码将打印进度指示器 [加载方式...]每 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;
}

这是我做的一个简单的:

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

我非常简单的 C 解决方案:

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

输出:

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

当针对现代 CPU 编译时,memset应优化为 1 或 2 个矢量指令,比静态存储整个字符串要小得多,而且速度相同。

在我使用它的程序中,我喜欢以这种方式打印 0%,在调用 progressbar 的循环之前:

fprintf(stderr, "[%-" S(PBWIDTH) ".*s] %u%%", 0, "", 0);
<小时 />

如果您希望数字位于条形之前,请更改progressbar中的fprintf

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

如果您执行可选的 0% 位:

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

根据需要修改所有内容。 :)

也许这段代码会帮助你 -

#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 << "nn";
    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 << "nn";
}
int main()
{
    show_progress_bar(100, "progress" , '#');
}

很简单,你可以使用string的fill构造函数:

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

这将打印:

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

如果你在纯C中需要它

并且您希望能够在运行时自定义大小和填充字符:

#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 + ''
    lBuffer[cTotalLength] = ''; //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;
}

但是,如果您不需要在运行时自定义它:

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

我需要创建一个进度条,这里的一些答案会导致进度条闪烁或在完成后显示 100% 的缺失百分比。这是一个除了模拟 cpu 工作的版本之外没有循环的版本,它仅在下一个进度单元递增时打印。

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

相关内容

  • 没有找到相关文章

最新更新