倒带 std::cout 以回到行首

Posted

技术标签:

【中文标题】倒带 std::cout 以回到行首【英文标题】:Rewinding std::cout to go back to the beginning of a line 【发布时间】:2011-03-04 17:34:51 【问题描述】:

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

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

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

【问题讨论】:

【参考方案1】:

"\r" 应该适用于 windows 和 Mac OS X。

类似:

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

【讨论】:

... 和 Linux,以及几乎所有其他东西。我认为理论上仍然无法正确处理此问题的唯一平台是 Mac OS 的 pre-X 版本。 这实际上会打印“”会看到这个这个“ 我的意思是,我也希望它能够工作,但后来我把你的代码放在 cpp.sh 中,它返回“不会看到这个会看到这个”,见cpp.sh/3kgrm。这是cpp.sh方面的错误吗?认为他们使用最新的标准编译器。 不要 flushendl 之前,因为它包含一个隐含的flush。事实上,当你不必立即输出时,根本不要使用endlflush。对于换行符,只需使用'\n'。流最迟在销毁时会刷新。 @Aziuth:问题不在于编译器,而是 cpp.sh 获取进程标准输出并将其呈现在浏览器中的代码不支持 \r ,就像普通的命令行终端一样.【参考方案2】:

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

我很确定 Mac 对待回车和换行的方式与 *nix 和 windows 不同。

如果您正在寻找就地更新(例如覆盖当前行),我建议您查看 curses 库。这应该提供一个独立于平台的方法来做你正在寻找的事情。 (因为,即使使用标准 C++,也没有与平台无关的方法来满足您的要求)。

【讨论】:

Mac 使用 将 CR 与 Win/Unix 区别开来,当时它还不是 Unix 本身。但现在是这样,这个问题已经消失了。【参考方案3】:

正如 Nathan Ernst 的回答所说,如果你想要一个健壮、正确的方法来做到这一点,请使用 curses - 特别是 ncurses。

如果您想要一种容易奏效且省力的 hackish 方式,请继续...

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

你通常想做的是……

int line_width = getenv("COLUMNS") ? atoi(getenv("COLUMNS")) : 80;
std::string spacesline_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 个空格,当自动 - flushed 最终会清除屏幕上该行的前 15 个字符,并保留前一个文件名的其余部分(如果它长于 15 个字符),然后等到缓冲区再次满后再更新屏幕(仍然是随意的) .

最后的std::endl 只是将光标从你打印文件名的那一行移开,这样你就可以写“全部完成”,或者离开main() 并让shell 提示符显示在一个漂亮干净的行上,而不是可能覆盖您最后一个文件名的一部分(像 zsh 这样的伟大 shell 会检查这个)。

【讨论】:

以上是关于倒带 std::cout 以回到行首的主要内容,如果未能解决你的问题,请参考以下文章

std::cout 不打印我想要的? [复制]

为啥我们需要绑定 std::cin 和 std::cout?

如何轻松使 std::cout 线程安全?

使用 std::cout 评估参数的顺序

使用 std::cout 评估参数的顺序

C++,三元运算符,std::cout