将std::cout倒回到行首

30

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

相反,我想使用单行输出文件路径,并将该行用于下一个文件。是否有字符(或其他代码)可输出到 std::cout 以实现此目的?

另外,如果我想重新针对 Windows 调整此工具,两个平台的解决方案是否相同?

3个回答

36
"\r" 应该适用于Windows和Mac OS X。
类似以下内容:
std::cout << "will not see this\rwill see this" << std::flush;
std::cout << std::endl; // all done

1
...和Linux,以及几乎所有其他平台。我认为唯一一个理论上仍可能无法正确处理这个的平台是Mac OS的X版本之前的版本。 - Pavel Minaev
3
实际上,这将打印出“will see thisthis”。 - Algoman
我的意思是,我也本来期望那样做可以行得通,但后来我把你的代码放到cpp.sh上运行,它返回了"will not see thiswill see this",请参见http://cpp.sh/3kgrm。这是cpp.sh的错误吗?我想他们使用最新的标准编译器。 - Aziuth
1
不要在endl之前使用flush,因为它包含了一个隐式的flush。实际上,只要不必立即输出,就不要使用endlflush。对于换行,只需使用'\n'。流将在最后被销毁时刷新。 - eike
@Aziuth:问题不在编译器上,而是cpp.sh代码无法像普通命令行终端一样支持\r来获取进程标准输出并在浏览器中呈现。 - Tony Delroy

2
我没有Mac电脑,但从控制台角度来看,这很大程度上取决于它如何处理回车和换行符。如果您可以向控制台发送其中之一,您需要发送回车符。
我相信Mac对待回车和换行符的方式与*nix和windows不同。
如果您正在寻找就地更新(例如覆盖当前行),我建议查看库。这应该提供了一个平台无关的方法来完成你想要的事情。(因为即使使用标准C ++,也没有实现你所要求的平台无关的方法)。

在Mac成为Unix操作系统之前,它曾经与Win/Unix不同地处理CR。但现在它已经是Unix了,这个问题已经不存在了。 - Pavel Minaev

0
如Nathan Ernst的回答所述,如果您想要一种强大、正确的方法来实现这一点,请使用curses - 具体来说是ncurses
如果您想要一种低成本的hackish方式,那么继续吧...
Linux、UNIX、MacOS、Windows等命令行终端通常支持一小组基本ASCII控制字符,包括十进制字符13 - 称为回车符,并在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++ 程序调用操作系统的 write() 函数,在开始处理文件之前将文本发送到终端。如果没有这个函数,需要更新的文本 - \r、空格、\r 和文件名 - 将被附加到缓冲区中,并且只有在缓冲区满时才会将其写入操作系统(例如以 4k 块为单位),因此显示的文件名将落后于实际正在处理的文件数十个。此外,假设缓冲区大小为 4k - 4096 字节 - 并且在某个时刻您已经缓冲了 4080 字节,然后输出下一个文件名的文本:您将得到 \r 和 15 个空格适合缓冲区,当自动刷新时,它将擦除屏幕上行的前 15 个字符并留下先前文件名的其余部分(如果它比 15 个字符长),然后等待缓冲区再次填满才更新屏幕(仍然是杂乱无章的)。

最后的std::endl只是将光标移动到你已经打印文件名的行之后,这样你就可以写"all done",或者直接离开main(),让shell提示显示在一个漂亮干净的行上,而不是潜在地覆盖你最后一个文件名的一部分(像zsh这样的好shell会检查这个问题)。

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