我正在用 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;
[> ] 0 %
[===========> ] 15 %
[======================> ] 31 %
[=================================> ] 47 %
[============================================> ] 63 %
[========================================================> ] 80 %
[===================================================================> ] 96 %
请注意,此输出显示彼此下一行,但在终端模拟器中(我认为也在Windows命令行中)它将被打印在同一行。
最后,不要忘记在打印更多内容之前打印换行符。
如果您想删除末尾的横线,则必须用空格覆盖它,以打印更短的内容,例如
"Done."
。
此外,当然可以使用 C 中的
printf
来完成同样的操作;调整上面的代码应该是直接的。
对于具有可调节进度条宽度的
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% [|||||||||||||||||||||||||||||||||||||||||| ]
您可以使用“回车符”( ) 不带换行符 ( ),并希望您的控制台做正确的事情。
您可以打印回车符 (
\r
) 将输出“光标”移回到当前行的开头。
对于更复杂的方法,请查看 ncurses(用于基于控制台文本的界面的 API)之类的东西。
看看boost Progress_display
http://www.boost.org/doc/libs/1_52_0/libs/timer/doc/original_timer.html#Class%20progress_display
我认为它可以满足您的需要,并且我相信它只是一个标头库,因此无需链接
我知道我回答这个问题有点晚了,但我制作了一个简单的类,它完全符合你的要求。(请记住,我在此之前写过
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 个向量指令,比静态存储整个字符串要小得多,而且速度同样快。
在我使用它的程序中,我喜欢在调用
progressbar
的循环之前以这种方式打印 0%:
fprintf(stderr, "[%-" S(PBWIDTH) ".*s] %u%%", 0, "", 0);
如果您希望数字位于横线之前,请更改
fprintf
中的 progressbar
:
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 << "\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" , '#');
}
很简单,你可以使用字符串的 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 + '\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;
}
但是如果你不需要在运行时自定义它:
#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));
}
}
我提供了一种使用 std::setfill() 和 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;
}