Appearance
[English version](/posts/c教程/en-US/05-Input Output and Files/044-Working with stdin, stdout, and stderr)
每个 C 程序都会自动启动并连接到您的环境、键盘、终端屏幕和错误控制台的三个开放流。它们是标准 I/O 流,使您的程序灵活且可编写脚本。
理解这三个流对于编写可以与文件、管道和其他程序交互的工具至关重要,这也是 Unix 风格设计的本质。
三种标准流
| 流 | 目的 | 典型器件 | 使用示例 |
|---|---|---|---|
stdin | 标准输入 | 键盘(或通过文件<) | scanf,fgets |
stdout | 标准输出 | 屏幕(或文件通过>) | printf,puts,fprintf(stdout, …) |
stderr | 标准错误 | 屏幕(与标准输出分开) | fprintf(stderr, …) |
这些都是类型FILE *。您可以将它们视为普通文件指针,读取、写入或重定向它们。
基本示例
#include <stdio.h>
int main(void) {
char name[50];
printf("Enter your name: ");
fgets(name, sizeof(name), stdin);
fprintf(stdout, "Hello, %s", name);
fprintf(stderr, "Note: This is an example error message.\n");
return 0;
}输出:
Enter your name: Alice
Hello, Alice
Note: This is an example error message.Shell 中的重定向
您可以分别重定向每个流:
./program < input.txt > output.txt 2> errors.log-<取代stdin(从文件读取输入) ->取代stdout(将正常输出写入文件) -2>取代stderr(将错误写入文件)
您还可以将它们组合起来:
./program > all_output.txt 2>&1这会将输出流和错误流合并到一个文件中。
从标准输入读取
您可以构建动态处理输入的程序,一次一行:
#include <stdio.h>
int main(void) {
char line[100];
printf("Enter text (Ctrl+D to stop):\n");
while (fgets(line, sizeof(line), stdin))
printf("You said: %s", line);
return 0;
}现在你的程序的行为就像一个 Unix 过滤器,它可以交替地从文件、管道或键盘输入中读取。
例子:
echo "hello" | ./program输出:
You said: hello写入 stdout 和 stderr
stdout用于正常程序输出,而stderr用于错误消息或日志。
#include <stdio.h>
int main(void) {
fprintf(stdout, "Everything is fine.\n");
fprintf(stderr, "Warning: something might be wrong.\n");
return 0;
}您可以抑制正常输出但保留错误:
./program > /dev/null输出:
Warning: something might be wrong.小代码:使用 stdin/stdout 的字计数器
这个小程序模仿了 Unix 的简化版本wc命令。
#include <stdio.h>
#include <ctype.h>
int main(void) {
int ch, words = 0, in_word = 0;
while ((ch = getchar()) != EOF) {
if (isspace(ch))
in_word = 0;
else if (!in_word) {
in_word = 1;
words++;
}
}
printf("Word count: %d\n", words);
return 0;
}尝试:
echo "C is small but powerful" | ./wordcount输出:
Word count: 4混合标准输出和标准错误
有时您需要将进度记录到stderr同时将结果输出到stdout。这样,日志就不会污染实际数据。
#include <stdio.h>
int main(void) {
for (int i = 0; i < 3; i++) {
fprintf(stderr, "Processing item %d...\n", i + 1);
fprintf(stdout, "Item %d processed\n", i + 1);
}
return 0;
}单独重定向日志:
./program > result.txt 2> log.txt冲洗缓冲器
输出流被缓冲,直到缓冲区已满或刷新时才会写入数据。为了确保输出立即出现:
fflush(stdout); // flush output buffer这对于交互式程序很有用。
为什么它很重要
这三个流为您的程序提供了灵活性:
- 交互式工作(键盘/屏幕)
- 批量工作(文件输入/输出)
- 使用管道与其他工具连锁
它们是 Unix 哲学的基础:做好一件事并且可以组合在一起的小程序。
自己尝试一下
- 编写一个从 stdin 读取并仅打印包含给定关键字的行的程序。
- 如果未提供关键字,则将错误打印到 stderr。
- 使用重定向文件的输入和输出
<和>. - 将进度消息添加到 stderr 并将其重定向到单独的日志。
- 将所有内容组合成一个小型“过滤器”工具,用于处理来自管道的文本。
和stdin,stdout, 和stderr,您的 C 程序成为无缝融入实际工作流程的工具,能够与文件、其他程序和用户等进行交互。接下来,您将探索缓冲 I/O,了解 C 库如何使用读取和写入缓冲区来优化性能fgets,fputs,等等。
