C++ 中的 I/O 重定向
在 C++ 中,输入和输出通过"流"(stream)完成。流就是字节序列。常见的流对象有:
cin:从键盘读取输入cout:把输出显示到屏幕
每个流都带有一个"缓冲区"(buffer),用来临时存放数据,让输入输出更快。
"I/O 重定向" 就是改变程序"从哪儿读"或"写到哪儿"。例如,把本来输出到屏幕的内容写进文件。
使用 ios::rdbuf() 进行重定向
函数 ios::rdbuf() 可以把流重定向到别的源头或目的地,让程序读写文件,而不是默认的键盘和屏幕。
示例代码:
cpp
#include <bits/stdc++.h>
using namespace std;
int main()
{
ofstream file; // 1. 创建一个输出文件流
file.open("writeFile.txt");
streambuf *cout_buf = cout.rdbuf(); // 2. 保存 cout 原来的缓冲区指针,以便后续恢复
streambuf *file_buf = file.rdbuf(); // 3. 拿到文件流对应的缓冲区指针
cout.rdbuf(file_buf); // 4. 把 cout 的缓冲区换成文件的缓冲区,实现重定向
// 5. 此时用 cout 输出的内容会写进文件 writeFile.txt
cout << "This line will be written to " << "'writeFile.txt'";
// 6. 手动刷新,确保数据真正写进文件
cout.flush();
// 7. 把 cout 的缓冲区恢复成默认(屏幕)
cout.rdbuf(cout_buf);
// 8. 这条信息会正常显示在控制台
cout << "This line will be writtein to console";
// 9. 关闭文件流
file.close();
return 0;
}
程序运行后屏幕实际只会看到:
arduino
This line will be writtein to console
而文件 writeFile.txt 里会多出:
arduino
This line will be written to 'writeFile.txt'
使用 freopen() 进行重定向
这是 C++ 里另一种实现 I/O 重定向的办法,但它其实是从 C 语言继承过来的。
语法:
cpp
freopen(fileName, mode, stream);
参数说明:
fileName:要重定向到的文件路径。mode:文件打开模式,例如只读"r"、写入"w"等。stream:要被重定向的标准流指针,如stdout、stdin、stderr。
返回值:
成功时返回指向新文件流的指针;失败返回 NULL。
示例代码:
cpp
#include <bits/stdc++.h>
using namespace std;
int main()
{
// 1. 把标准输出(stdout)重定向到文件 "output_freopen.txt",模式为写入("w")
if (freopen("output_freopen.txt", "w", stdout) == NULL)
{
cerr << "重定向 stdout 失败!" << endl;
}
// 2. 此时用 cout 输出的内容都会写进文件,而不会出现在控制台
cout << "This output will be written to 'output_freopen.txt'" << endl;
cout << "Another line to the file.";
return 0;
}
程序运行后屏幕不会显示任何内容, 但文件 output_freopen.txt 里会出现:
vbnet
This output will be written to 'output_freopen.txt'
Another line to the file.
为什么需要 I/O 重定向
把标准输入输出"换道"后,程序能获得以下好处:
- 自动化:同一份输入数据可反复跑,无需手工敲键盘,批量测试、脚本处理必备。
- 数据管道:一个程序的输出直接当另一个程序的输入,命令行里轻松搭起流水线。
- 测试与调试:换文件就能换测试场景;把输出抓进文件,随时比对预期结果。
- 日志与监控:把正常输出或错误信息重定向到日志文件,方便排错、审计和长期监控。
- 控制台清爽:后台跑程序时屏幕保持干净,结果事后看文件即可。
ios::rdbuf() 与 freopen() 的区别
| ios::rdbuf() | freopen() |
|---|---|
用于获取或更换 istream/ostream 的内部缓冲区指针。 |
把已存在的文件"绑"到标准流(stdin/stdout/stderr)上。 |
| 只改流对象内部缓冲区,文件描述符不变。 | 连文件描述符一起换掉,真正重定向到指定文件。 |
| 可对缓冲机制做细粒度控制(如切回原来缓冲区)。 | 仅完成"文件↔流"的映射,不直接操作缓冲区。 |
| 属于 C++ 风格的流库接口。 | 继承自 C 标准库,跨 C/C++ 通用。 |
C++ 文件流 API简单总结
C++ 文件流 API 一览(按"干什么"分类,只列标准库 <fstream> + <filesystem> 高频接口,C++17 及以上标⭐)
1. 打开/关闭/文件状态
| 目的 | 关键成员 |
|---|---|
| 构造函数即打开 | std::ifstream/ofstream/fstream 带路径构造 |
| 事后打开 | .open(path, mode) |
| 关闭 | .close() |
| 是否打开成功 | operator! / .fail() / .is_open() |
| 文件是否存在⭐ | std::filesystem::exists(path) |
| 文件大小⭐ | std::filesystem::file_size(path) |
2. 读写定位
| 目的 | 关键成员 |
|---|---|
| 读位置 | .tellg() |
| 写位置 | .tellp() |
| 跳读 | .seekg(offset, dir) |
| 跳写 | .seekp(offset, dir) |
| dir 枚举 | std::ios::beg/cur/end |
3. 按字符/行/块 读写
| 方式 | 关键调用 |
|---|---|
| 单字符 | stream.get(c) / stream.put(c) |
| 行 | std::getline(stream, string) |
| 整块 | stream.read(buf, n) / stream.write(buf, n) |
| 已读字节数 | stream.gcount() |
4. 格式化写入,读取
| 目的 | 用法 |
|---|---|
| 与控制台语法一致 | 直接用 读【>>】 , 写【 <<】 |
5. 缓冲区底层控制
| 目的 | 关键调用 |
|---|---|
| 换缓冲区 | stream.rdbuf(new_buf) |
| 取当前缓冲区 | stream.rdbuf() |
| 手动刷盘 | stream.flush() |
| 与 std::cout 同步开关 | std::ios::sync_with_stdio(bool) |
6. 打开模式位(可组合)
| 位 | 含义 |
|---|---|
std::ios::in |
读 |
std::ios::out |
写(默认 truncate) |
std::ios::app |
追加 |
std::ios::ate |
打开后指针置尾 |
std::ios::trunc |
若已存在则截空 |
std::ios::binary |
二进制模式 |
7. 错误与状态检测
| 状态函数 | 含义 |
|---|---|
.good() |
一切正常 |
.eof() |
读到 EOF |
.fail() |
格式/逻辑错误 |
.bad() |
严重错误(设备故障) |
.clear(flags=std::ios::goodbit) |
手动清状态 |
8. 文件系统级操作⭐(<filesystem>)
| 任务 | 调用 |
|---|---|
| 创建目录 | std::filesystem::create_directory/parent_path |
| 拷贝文件 | std::filesystem::copy_file |
| 改名/移动 | std::filesystem::rename |
| 删除 | std::filesystem::remove/all |
| 遍历目录 | std::filesystem::directory_iterator |
| 路径拼接 | operator/ 或 std::filesystem::path::append |