C++控制台进度指示器

8
有没有一种简单的方法来为正在执行的任务实现基于控制台的进度指示器,但我无法预测需要多长时间?
以前我在编写Clipper代码时就这样做过,只需要迭代字符'/','-','\'和'|'并将它们放置在同一个位置即可。
有没有什么方法/链接/库可以在C++中实现类似的功能?
这个目标是*nix环境。
编辑:
- 更改标题以使其更连贯和通用; - 添加目标环境。

1
你为什么不在C++控制台应用程序中做同样的事情呢? - Tal Pressman
@Tal:我在C++和控制台应用程序方面有点新手,所以我需要有人指引我正确的方向,提供一些元代码或关于这个主题的好链接。 - kolrie
2
https://dev59.com/YknSa4cB1Zd3GeqPLiov#1365329 - Khaled Alshaya
4个回答

24

一种非常简单的方法是打印一个字符串,然后跟随一个 '\r' 字符。这是回车字符本身,在大多数控制台上,它会将光标返回到行首而不向下移动。这使您可以覆盖当前行。

如果您正在写入 stdout、cout 或 clog,请记得 fflush 或 std::flush 流以立即输出该行。如果您正在写入 stderr 或 cerr,则该流是无缓冲的,并且所有输出都是立即的(而且效率低下)。

更复杂的方法是使用屏幕绘图库,例如 curses。Windows 控制台有其他一些方法可以设置它们以进行直接屏幕编写,但我不知道它们是什么。


谢谢Zan,'\r'技巧听起来值得一试! - kolrie
正如Zan所建议的那样,我会使用curses。快速搜索“curses教程”将为您提供所需的一切... - Tal Pressman
解决方案可行,正是我所需要的——最简单的可行情况。 - kolrie
如果选择Windows特定的解决方案,那么就没有必要使用conio.h(因为它本来就是平台特定的)- 只需使用Win32 API控制台函数即可。 - Pavel Minaev

10
你可以尝试以下方法:

你可以尝试类似这样的操作:

void
spinner(int spin_seconds) {
    static char const spin_chars[] = "/-\\|";
    unsigned long i, num_iterations = (spin_seconds * 10);
    for (i=0; i<num_iterations; ++i) {
        putchar(spin_chars[i % sizeof(spin_chars)]);
        fflush(stdout);
        usleep(100000);
        putchar('\b');
    }
}

当然,由于子秒级的usleep(),这是非标准的,我不确定\b是否保证擦除一个字符,但它在大多数平台上都有效。如果\b无法实现,请尝试使用\r。否则,尝试找到curses的版本。

编辑(curses示例)

这应该可以帮助您入门:

#include <curses.h>
#include <unistd.h>

void spinner(int spin_seconds) {
    static char const spin_chars[] = "/-\\|";
    unsigned long i, num_iterations = (spin_seconds * 10);
    for (i=0; i<num_iterations; ++i) {
        mvaddch(0, 0, spin_chars[i & 3]);
        refresh();
        usleep(100000);
    }
}

int main() {
    initscr();    /* initializes curses */
    spinner(10);  /* spin for 10 seconds */
    endwin();     /* cleanup curses */
    return 0;
}

请确保链接了-lcurses-lncurses。这在任何类UNIX系统上都应该工作。

非常棒,非常感谢。我会进行一些实验来学习这个,因为\r的解决方案对我来说已经足够了。这是唯一让我没有选择你详细解释的答案的原因。谢谢! - kolrie

1
Boost有一个进度库,可以帮助处理这些事情。

0

哇,clipper,也许你是在谈论语言中内置的@row,col吗?(仅仅是修辞问题...)

使用printf可以创建简单的进度条:可以省略尾随的换行符。显然,您可以以\b开始或结束字符串,以便覆盖字符。这样很容易做传统的-\|/类型。

我记得Eclipse UI指南建议使用进度指示器,而不管您能够告诉实际进度的多少。我认为理论是任何东西都比没有好,尽你所能做最好的。

您可能需要的唯一技巧是潜在地打败了行缓冲。确保在每次输出操作之后使用fflush(stdout)。(或ostream::flush())


如果您不介意我回答您的修辞问题,是的,我们曾经做过类似的事情。好旧的Clipper时代 :-) - kolrie
我曾经想知道为什么我的程序不需要刷新,后来我意识到我正在使用未缓冲的stderr。 - Zan Lynx

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接