【问题标题】:How to display a progress indicator in pure C/C++ (cout/printf)?如何在纯 C/C++ (cout/printf) 中显示进度指示器?
【发布时间】:2013-01-10 11:35:30
【问题描述】:

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

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

【问题讨论】:

标签: c++ c user-interface c++11 io


【解决方案1】:

使用固定宽度的输出,使用如下内容:

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 来完成相同的操作;修改上面的代码应该是直截了当的。

【讨论】:

    【解决方案2】:

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

    【讨论】:

    • + 手动刷新,否则不会立即显示,因为输出是缓冲的。
    • 如果用户不小心按了回车键,它就会崩溃:(除此之外,它可能是最便携的解决方案,+1。
    • @Ali 为避免这种情况,您必须禁用回显(请参阅man termios
    • @leemes #include &lt;termios.h&gt;,在 M$ Windows 上试试 :) 无论如何,谢谢你的提示,我可能会在 Linux 上试试。
    • @Ali W1ndOw$ 可能有等价物,但我不知道。 ;)
    【解决方案3】:

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

    【讨论】:

    • 注意,第一个#define的末尾应该没有';'
    • 这是迄今为止我找到的最简单最好的解决方案
    • 有没有办法显示 ascii 字符而不是 | ?谢谢
    • 最后一部分是什么意思,为什么printf()的最后一个参数有""
    • @Sylvain 方括号中的字符串有两部分;左和右。左边部分由PBSTRlpad 字符组成,使用%.*s 说明符打印,而右边部分由rpad 空格左填充字符串的长度组成,我们选择为空“”,这样我们只使用 %*s 说明符打印 rpad 空格。
    【解决方案4】:

    看看 boost progress_display

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

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

    【讨论】:

      【解决方案5】:

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

      要了解更复杂的方法,请查看 ncurses(基于控制台文本的界面的 API)之类的东西。

      【讨论】:

      • + 手动刷新,否则不会立即显示,因为输出是缓冲的。
      • + '\b' 将光标向左移动一位。
      • ncurses 为 +1。如果您想做一些更复杂的事情,绝对是要走的路。
      【解决方案6】:

      我知道我回答这个问题有点晚了,但我写了a simple class that does exactly what you want.(请记住,我在此之前写了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;
      }
      

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

      【讨论】:

        【解决方案7】:

        另一种方式可能是显示“点”或您想要的任何字符。下面的代码将每隔 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;
        }
        

        【讨论】:

          【解决方案8】:

          这是我做的一个简单的:

          #include <iostream>
          #include <windows.h>
          
          using namespace std;
          
          int barl = 20;
          
          int main() {
             system("color 0e");  
             cout << "[";     
             for (int i = 0; i < barl; i++) {         
                Sleep(100);       
                cout << ":";  
             }
             cout << "]";
          }
          

          【讨论】:

            【解决方案9】:

            也许这段代码会对你有所帮助 -

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

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2020-09-24
              • 2011-09-29
              • 1970-01-01
              • 2016-05-08
              • 2010-11-28
              相关资源
              最近更新 更多