探索文本与二进制文件操作的两种主要方法:freopen 与 C++ 文件流 (ifstream/ofstream/fstream)
freopen 方法:C 语言函数,用于重定向标准输入 (stdin)、标准输出 (stdout)、标准错误 (stderr) 到文件。常用于竞赛或快速脚本。ifstream:Input File Stream,用于从文件读取。ofstream:Output File Stream,用于向文件写入。fstream:File Stream,可同时读写文件。freopen 简单直接,fstream 类功能更全、更安全。文本模式 (Text Mode):
\n 会被转换为 \r\n 写入,读取时反向转换。二进制模式 (Binary Mode):
ios::binary 标志。ios::binary。fstream 对象在销毁时通常会自动关闭)。文件流对象有状态标志位,可通过成员函数检查:
is_open(): 文件是否成功打开。good(): 流是否处于可用状态 (没有错误)。fail(): 发生非致命错误 (如格式错误),流可能仍可恢复。bad(): 发生严重错误 (如读写硬件问题),流不可恢复。eof(): End Of File, 已到达文件末尾。operator!() 或 operator bool(): 重载了逻辑非和布尔转换,if (!file) 或 if (file) 可直接判断流状态是否良好 (非 fail 或 bad)。clear(): 清除流的错误状态标志位 (如 eof 或 fail),使流可能恢复操作。if (!file) 或 if (file.fail()) 检查操作是否成功。freopen 进行文件操作 (C 风格)主要通过重定向标准输入/输出流 (stdin, stdout) 到文件,然后像操作控制台一样使用 scanf/printf 或 C++ 的 cin/cout。
// --- 文本写入示例 ---
// 重定向标准输出 stdout 到 "output.txt" 文件
if (freopen("output.txt", "w", stdout) == NULL) {
perror("错误:无法重定向 stdout");
exit(1); // 失败则退出
}
// 现在 printf 或 cout 的输出会进入 "output.txt"
printf("这是写入文件的内容。\n");
cout << "使用 cout 写入也可以。" << endl;
// !! 注意:关闭文件通常是通过关闭程序或重定向回控制台
// 恢复 stdout 到控制台 (不同系统方法不同)
freopen("/dev/tty", "w", stdout); // Unix/Linux/macOS
// freopen("CON", "w", stdout); // Windows
// --- 文本读取示例 ---
// 重定向标准输入 stdin 到 "input.txt" 文件
if (freopen("input.txt", "r", stdin) == NULL) {
perror("错误:无法重定向 stdin");
exit(1);
}
int number;
char str[100];
// scanf 或 cin 会从 "input.txt" 读取
scanf("%d %s", &number, str);
cin >> number >> str; // 同样有效
// 恢复 stdin (同上)
freopen("/dev/tty", "r", stdin); // Unix/Linux/macOS
// freopen("CON", "r", stdin); // Windows
注意:freopen 直接修改全局流,可能影响程序其他部分,且错误处理不如 C++ 流健壮。
ifstream/ofstream/fstream 进行文件操作 (C++ 风格)创建文件流对象,关联特定文件进行操作。更符合面向对象思想,提供更丰富的状态检查和控制。
// --- 文本写入示例 (ofstream) ---
ofstream outFile("output.txt"); // 创建输出流对象并尝试打开文件
// 也可以分开: ofstream outFile; outFile.open("output.txt");
if (!outFile.is_open()) { // 检查是否成功打开
cerr << "错误:无法打开文件 " << "output.txt" << " 进行写入" << endl;
// 处理错误...
} else {
outFile << "使用 C++ ofstream 写入文件。" << endl;
outFile << 12345 << " " << 3.14;
outFile.close(); // 主动关闭 (对象析构时也会自动关闭)
if (outFile.fail()) { // 检查关闭/写入过程中是否有错误
cerr << "写入或关闭文件时发生错误。" << endl;
}
}
// --- 文本读取示例 (ifstream) ---
ifstream inFile("input.txt"); // 创建输入流对象并尝试打开
if (!inFile) { // 使用重载的 operator! 检查打开和状态
cerr << "错误:无法打开文件 " << "input.txt" << " 进行读取" << endl;
} else {
string line;
// 逐行读取
while (getline(inFile, line)) {
cout << "读取到行: " << line << endl;
}
// 或按类型读取
// inFile.clear(); // 如果之前 getline 到达eof, 需要清除状态
// inFile.seekg(0); // 回到文件开头
int number;
string word;
if (inFile >> number >> word) { // 检查读取是否成功
cout << "读取到数字: " << number << ", 单词: " << word << endl;
} else {
if (inFile.eof()) cerr << "已到文件末尾。" << endl;
if (inFile.fail()) cerr << "读取格式错误。" << endl;
}
inFile.close();
}
假设 input.txt 文件内容为 42。
// --- 方法一:freopen + cin ---
#include <iostream>
#include <fstream>// 包含文件流头文件
#include <string>// 如果需要读取字符串等
#include <cstdio> // freopen 在 cstdio 或 stdio.h 中
using namespace std;
int main_freopen() {
if (freopen("input.txt", "r", stdin) == NULL) {
perror("freopen 失败");
return 1;
}
int x;
cin >> x; // 从 input.txt 读取
cout << "freopen 读取到: " << x << endl; // 输出到控制台 (如果stdout未重定向)
// 恢复 stdin (如果需要后续控制台输入)
freopen("/dev/tty", "r", stdin); // 或 "CON"
return 0;
}
// --- 方法二:ifstream ---
int main_ifstream() {
ifstream inputFile("input.txt"); // 创建对象并打开
if (!inputFile) { // 检查打开和初始状态
cerr << "ifstream 打开失败" << endl;
return 1;
}
int y;
inputFile >> y; // 从 inputFile 对象读取
if (inputFile.fail() && !inputFile.eof()) { // 检查读取是否成功 (排除正常EOF)
cerr << "ifstream 读取失败" << endl;
inputFile.close();
return 1;
}
cout << "ifstream 读取到: " << y << endl; // 输出到控制台
inputFile.close(); // 关闭文件
return 0;
}
freopen: 简单,代码改动小 (尤其对于习惯用 cin/cout 的场景),但全局影响,错误处理较弱,不面向对象。适合快速原型、算法竞赛。fstream 类: C++ 标准方式,面向对象,类型安全,错误处理机制完善,功能丰富 (二进制、随机访问等),更适合大型项目和健壮的应用程序。ios::binary 模式。read(char* buffer, streamsize count) 读取指定字节数到字符缓冲区。write(const char* buffer, streamsize count) 从字符缓冲区写入指定字节数。struct) 或类对象 (需要注意内存布局和指针)。
// 写入结构体
struct Point { int x, y; };
Point p = {10, 20};
ofstream binOut("point.dat", ios::binary);
binOut.write(reinterpret_cast<const char*>(&p), sizeof(Point));
binOut.close();
// 读取结构体
Point p_read;
ifstream binIn("point.dat", ios::binary);
binIn.read(reinterpret_cast<char*>(&p_read), sizeof(Point));
binIn.close();
seekg(offset, direction): 移动读指针 (Get pointer)。seekp(offset, direction): 移动写指针 (Put pointer)。 (通常读写指针一起移动)tellg() / tellp(): 获取当前读/写指针位置。ios::beg: 从文件开头计算偏移。ios::cur: 从当前位置计算偏移。ios::end: 从文件末尾计算偏移 (通常 offset 为负)。endl 不仅插入换行符,还会刷新 (flush) 输出缓冲区。file.flush(): 手动强制将缓冲区内容写入文件。close() 或对象析构) 会自动刷新缓冲区。| (位或) 组合使用
ios::in: 读 (ifstream 默认)。ios::out: 写 (ofstream 默认),会清空文件内容。ios::app: 追加 (Append),在文件末尾写入,不清空。ios::binary: 二进制模式。ios::trunc: 截断 (Truncate),打开即清空文件 (ofstream 默认行为之一,除非指定 app 或 in)。ios::ate: (At End),打开后文件指针定位到末尾,但可以移动。