如何在 C 中覆盖标准输出
Posted
技术标签:
【中文标题】如何在 C 中覆盖标准输出【英文标题】:How to overwrite stdout in C 【发布时间】:2010-10-13 23:29:38 【问题描述】:在大多数现代 shell 中,您可以点击向上和向下箭头,它会在提示符处显示您之前执行的命令。我的问题是,这是如何工作的?!
在我看来,shell 正在以某种方式操纵 stdout 以覆盖它已经写入的内容?
我注意到像 wget 这样的程序也可以做到这一点。有人知道他们是怎么做到的吗?
【问题讨论】:
【参考方案1】:它不是在操作标准输出——它是在覆盖终端已经显示的字符。
试试这个:
#include <stdio.h>
#include <unistd.h>
static char bar[] = "======================================="
"======================================>";
int main()
int i;
for (i = 77; i >= 0; i--)
printf("[%s]\r", &bar[i]);
fflush(stdout);
sleep(1);
printf("\n");
return 0;
这与wget
的输出非常接近,对吧? \r
是一个回车,终端将其解释为“将光标移回当前行的开头”。
如果是bash
,您的shell 使用GNU Readline library,它提供了更通用的功能,包括检测终端类型、历史管理、可编程键绑定等。
还有一件事——如果有疑问,您的 wget、shell 等的源代码都是可用的。
【讨论】:
【参考方案2】:要覆盖当前标准输出行(或部分),请使用\r
(或\b
。)特殊字符\r
(回车)会将插入符号返回到行首,允许您覆盖它。特殊字符\b
只会将插入符号带回一个位置,允许您覆盖最后一个字符,例如
#include <stdio.h>
#include <unistd.h>
int i;
const char progress[] = "|/-\\";
for (i = 0; i < 100; i += 10)
printf("Processing: %3d%%\r",i); /* \r returns the caret to the line start */
fflush(stdout);
sleep(1);
printf("\n"); /* goes to the next line */
fflush(stdout);
printf("Processing: ");
for (i = 0; i < 100; i += 10)
printf("%c\b", progress[(i/10)%sizeof(progress)]); /* \b goes one back */
fflush(stdout);
sleep(1);
printf("\n"); /* goes to the next line */
fflush(stdout);
使用fflush(stdout);
,因为standard output is usually buffered,否则信息可能不会立即打印在输出或终端上
【讨论】:
可能想为睡眠功能添加一个#include除了 \r 和 \b 之外,请查看 ncurses 以获得对控制台屏幕上内容的一些高级控制。 (包括列,随意移动等)。
【讨论】:
【参考方案4】:在文本终端/控制台中运行的程序可以以各种方式操作在其控制台中显示的文本(使文本变为粗体、移动光标、清除屏幕等)。这是通过打印特殊字符序列来完成的,称为"escape sequences"(因为它们通常以 Escape 开头,ASCII 27)。
如果 stdout 进入一个能理解这些转义序列的终端,终端的显示会相应改变。
如果你将标准输出重定向到一个文件,转义序列将出现在文件中(这通常不是你想要的)。
转义序列没有完整的标准,但大多数终端使用VT100引入的序列,并有很多扩展。这是 Unix/Linux 下的大多数终端(xterm、rxvt、konsole)和其他类似 PuTTY 的终端都理解的。
实际上,您不会直接将转义序列硬编码到您的软件中(尽管您可以),而是使用库来打印它们,例如上面提到的ncurses 或GNU readline。这允许与不同的终端类型兼容。
【讨论】:
【参考方案5】:它是通过readline 库完成的...我不确定它在幕后是如何工作的,但我认为它与标准输出或流没有任何关系。我怀疑 readline 使用了某种神秘的(至少对我而言)终端命令——也就是说,它与实际显示你的 shell 会话的终端程序合作。我不知道您是否可以仅通过打印输出来获得类似 readline 的行为。
(想一想:stdout 可以重定向到文件,但向上/向下箭头键的技巧对文件不起作用。)
【讨论】:
【参考方案6】:你可以使用回车来模拟这个。
#include <stdio.h>
int main(int argc, char* argv[])
while(1)
printf("***********");
fflush(stdout);
sleep(1);
printf("\r");
printf("...........");
sleep(1);
return 0;
【讨论】:
【参考方案7】:程序通过打印终端以特殊方式解释的特殊字符来做到这一点。最简单的版本是(在大多数 linux/unix 终端上)打印 '\r' (回车)到正常的标准输出,它将光标位置重置为当前行中的第一个字符。所以你接下来写的东西会覆盖你之前写的那行。例如,这可用于简单的进度指示器。
int i = 0;
while (something)
i++;
printf("\rprocessing line %i...", i);
...
但是还有更复杂的转义字符序列,它们以各种方式解释。各种事情都可以用它来完成,比如将光标定位在屏幕上的特定位置或设置文本颜色。是否或如何解释这些字符序列取决于您的终端,但大多数终端支持的通用类是ansi escape sequences。因此,如果您想要红色文本,请尝试:
printf("Text in \033[1;31mred\033[0m\n");
【讨论】:
处理这些特殊字符的不是shell,而是shell运行的终端,即xterm、rxvt、konsole或类似的实例。【参考方案8】:最简单的方法是将回车符('\r')打印到标准输出。
光标将移动到行首,允许您覆盖其内容。
【讨论】:
以上是关于如何在 C 中覆盖标准输出的主要内容,如果未能解决你的问题,请参考以下文章