将std :: cout重绕以返回到行的开头

时间:2010-06-16 23:46:11

标签: c++ macos terminal stdout output-formatting

我正在为Mac OS X编写一个处理大量文件的命令行工具。我想向用户显示正在处理的当前文件,但不希望有一个bazillion文件污染终端窗口。

相反,我想使用一行输出文件路径,然后将该行重用于下一个文件。是否有一个字符(或其他代码)输出到std::cout来完成此操作?

另外,如果我想重新定位适用于Windows的此工具,两种平台的解决方案是否相同?

4 个答案:

答案 0 :(得分:19)

“\ r”应适用于Windows和Mac OS X.

类似的东西:

std::cout << "will not see this\rwill see this" << std::flush;
std::cout << std::endl; // all done

答案 1 :(得分:1)

我无法访问mac,但从纯控制台的角度来看,这在很大程度上取决于它如何处理回车符和换行符。如果你可以将一个或另一个发送到控制台,你想发送 一个回车。

我非常确定Mac对待回车和换行的方式与* nix&amp;窗户。

如果您正在寻找就地更新(例如覆盖当前行),我建议您查看curses lib。这应该提供一种平台独立的方式来做你正在寻找的东西。 (因为,即使使用标准的C ++,也没有平台独立的方式来满足你的需求)。

答案 2 :(得分:0)

正如内森·恩斯特(Nathan Ernst)的回答所说,如果您想要一种健壮,正确的方法来执行此操作,请使用诅咒-特别是ncurses

如果您希望采用一种省力的hacking方式,那么可以继续...

用于Linux,UNIX,MacOS,Windows等的命令行终端倾向于支持一小组基本的ASCII控制字符,包括13位十进制字符-被称为回车符,encoded in C++被称为'\ r'或等效的八进制'\ 015'或十六进制'\ x0D'-指示终端返回到行首。

您通常想做的是...

int line_width = getenv("COLUMNS") ? atoi(getenv("COLUMNS")) : 80;
std::string spaces{line_width - 1, ' '};
for (const auto& filename : filenames) {
    std::cout << '\r' << spaces << '\r' << filename << std::flush;
    process_file(filename);
}
std::cout << std::endl; // move past last filename...

在写入下一个文件名之前,它使用一串空格覆盖旧文件名,因此,如果文件名较短,则不会看到较早的文件名中的尾随字符。

std::flush确保C ++程序在开始处理文件之前先调用OS write()函数将文本发送到终端。否则,更新所需的文本{\r,空格,\r和文件名将被附加到缓冲区中,并且仅写入操作系统,例如。 4k块-缓冲区已满时,因此显示的文件名将使数十个文件滞后于正在处理的实际文件。此外,假设缓冲区为4k-4096字节-有时您要缓冲4080字节,然后输出下一个文件名的文本:您将以\r和15个空格填充缓冲区,当自动刷新将最终清除屏幕上该行的前15个字符,并保留前一个文件名的其余部分(如果长度超过15个字符),然后等待直到缓冲区再次满后再更新屏幕(仍然偶然地)。

最后的std::endl只是将光标从您一直在打印文件名的行上移开,这样您就可以编写“全部完成”,或者只留下main()并在命令行提示符处显示shell提示。简洁明了的行,而不是可能覆盖您的最后一个文件名的一部分(像zsh这样的大shell对此进行检查)。

答案 3 :(得分:-2)

std :: cout将“\ r”解释为返回到行的开头,如果您不想每次都添加“&lt;&lt; endl”,请使用“\ n”

std::cout << "this will work!\nSee... a new line!" << std::endl;