## 文件的概念 文件是根据特定的目的而收集在一起的有关数据的集合。C/C++ 把每一个文件都看成是一个有序的字节流,每个文件都是以 **文件结束标志**(EOF)结束,如果要操作某个文件,程序应该首先打开该文件,每当一个文件被打开后(请记得关闭打开的文件),该文件就和一个流关联起来,这里的流实际上是一个字节序列。 C/C++ 将文件分为文本文件和二进制文件。文本文件就是简单的文本文件(重点),另外二进制文件就是特殊格式的文件或者可执行代码文件等。 --- ## 文件的操作步骤 1、打开文件,将文件指针指向文件,决定打开文件类型; 2、对文件进行读、写操作(比赛中主要用到的操作,其他一些操作暂时不写); 3、在使用完文件后,关闭文件。 --- ## `freopen` 函数 ### 函数简介 函数用于将指定输入输出流以指定方式重定向到文件,包含于头文件 `stdio.h (cstdio)` 中,该函数可以在不改变代码原貌的情况下改变输入输出环境,但使用时应当保证流是可靠的。 函数主要有三种方式:读、写和附加。 --- ### 命令格式 ```cpp FILE* freopen(const char* filename, const char* mode, FILE* stream); ``` ### 参数说明 - `filename`: 要打开的文件名 - `mode`: 文件打开的模式,表示文件访问的权限 - `stream`: 文件指针,通常使用标准文件流 (`stdin/stdout`) 或标准错误输出流 (`stderr`) - 返回值:文件指针,指向被打开文件 --- ### 文件打开格式(选读) - `r`:以只读方式打开文件,文件必须存在,只允许读入数据 **(常用)** - `r+`:以读/写方式打开文件,文件必须存在,允许读/写数据 - `rb`:以只读方式打开二进制文件,文件必须存在,只允许读入数据 - `rb+`:以读/写方式打开二进制文件,文件必须存在,允许读/写数据 - `rt+`:以读/写方式打开文本文件,允许读/写数据 - `w`:以只写方式打开文件,文件不存在会新建文件,否则清空内容,只允许写入数据 **(常用)** - `w+`:以读/写方式打开文件,文件不存在将新建文件,否则清空内容,允许读/写数据 - `wb`:以只写方式打开二进制文件,文件不存在将会新建文件,否则清空内容,只允许写入数据 - `wb+`:以读/写方式打开二进制文件,文件不存在将新建文件,否则清空内容,允许读/写数据 - `a`:以只写方式打开文件,文件不存在将新建文件,写入数据将被附加在文件末尾(保留 EOF 符) - `a+`:以读/写方式打开文件,文件不存在将新建文件,写入数据将被附加在文件末尾(不保留 EOF 符) - `at+`:以读/写方式打开文本文件,写入数据将被附加在文件末尾 - `ab+`:以读/写方式打开二进制文件,写入数据将被附加在文件末尾 --- ### 使用方法 读入文件内容: ```cpp freopen("data.in", "r", stdin); // data.in 就是读取的文件名,要和可执行文件放在同一目录下 ``` 输出到文件: ```cpp freopen("data.out", "w", stdout); // data.out 就是输出文件的文件名,和可执行文件在同一目录下 ``` 关闭标准输入/输出流 ```cpp fclose(stdin); fclose(stdout); ``` 注 `printf/scanf/cin/cout` 等函数默认使用 `stdin/stdout`,将 `stdin/stdout` 重定向后,这些函数将输入/输出到被定向的文件 --- ### 模板 ```cpp #include
#include
int main(void) { freopen("data.in", "r", stdin); freopen("data.out", "w", stdout); /* 中间的代码不需要改变,直接使用 cin 和 cout 即可 */ fclose(stdin); fclose(stdout); return 0; } ``` --- ## `fopen` 函数(选读) 函数大致与 `freopen` 相同,函数将打开指定文件并返回打开文件的指针 ### 函数原型 ```cpp FILE* fopen(const char* path, const char* mode) ``` 各项参数含义同 `freopen` --- ### 可用读写函数(基本) - `fread/fwrite` - `fgetc/fputc` - `fscanf/fprintf` - `fgets/fputs` ### 使用方式 ```cpp FILE *in, *out; // 定义文件指针 in = fopen("data.in", "r"); out = fopen("data.out", "w"); /* do what you want to do */ fclose(in); fclose(out); ``` --- ## C++ 的 `ifstream/ofstream` 文件输入输出流 ### 使用方法 读入文件内容: ```cpp ifstream fin("data.in"); // data.in 就是读取文件的相对位置或绝对位置 ``` 输出到文件: ```cpp ofstream fout("data.out"); // data.out 就是输出文件的相对位置或绝对位置 ``` 关闭标准输入/输出流 ```cpp fin.close(); fout.close(); ``` --- ### 模板 ```cpp #include
using namespace std; // 两个类型都在 std 命名空间里 ifstream fin("data.in"); ofstream fout("data.out"); int main(void) { /* 中间的代码改变 cin 为 fin ,cout 为 fout 即可 */ fin.close(); fout.close(); return 0; } ``` --- # 使用 cin/cout 重定向实现文件输入输出 在 C++ 中,可以通过流重定向技术让 `cin` 和 `cout` 直接与文件交互,而不是标准输入输出设备。以下是详细实现方法: --- ## 基本原理 - `cin` 是 `istream` 类的对象,关联到标准输入 - `cout` 是 `ostream` 类的对象,关联到标准输出 - 通过 `rdbuf()` 函数可以重定向流的缓冲区 --- ## 重定向方法 ### 1. 保存原始缓冲区(可选但推荐) ```cpp #include
#include
int main() { // 保存原始缓冲区 std::streambuf* orig_cin = std::cin.rdbuf(); std::streambuf* orig_cout = std::cout.rdbuf(); // ... 重定向操作 ... // 恢复原始缓冲区 std::cin.rdbuf(orig_cin); std::cout.rdbuf(orig_cout); } ``` --- ### 2. 重定向到文件 #### 输入重定向(cin → 文件) ```cpp std::ifstream inFile("input.txt"); std::cin.rdbuf(inFile.rdbuf()); // 重定向cin到文件 // 现在所有cin操作都从input.txt读取 int value; std::cin >> value; // 从文件读取 ``` #### 输出重定向(cout → 文件) ```cpp std::ofstream outFile("output.txt"); std::cout.rdbuf(outFile.rdbuf()); // 重定向cout到文件 // 现在所有cout操作都写入output.txt std::cout << "Hello, File!"; // 写入文件 ``` --- ### 3. 同时重定向输入和输出 ```cpp std::ifstream in("input.txt"); std::ofstream out("output.txt"); // 重定向输入 std::cin.rdbuf(in.rdbuf()); // 重定向输出 std::cout.rdbuf(out.rdbuf()); // 现在所有I/O操作都针对文件 std::string data; while (std::cin >> data) { std::cout << "Processed: " << data << "\n"; } ``` --- ## 完整示例 ```cpp #include
#include
int main() { // 1. 保存原始缓冲区 std::streambuf* origCin = std::cin.rdbuf(); std::streambuf* origCout = std::cout.rdbuf(); // 2. 打开文件流 std::ifstream input("data.in"); std::ofstream output("result.out"); // 3. 重定向 std::cin.rdbuf(input.rdbuf()); std::cout.rdbuf(output.rdbuf()); // 4. 文件操作(使用cin/cout) int a, b; std::cin >> a >> b; // 从data.in读取 std::cout << "Sum: " << a + b; // 写入result.out // 5. 恢复原始缓冲区 std::cin.rdbuf(origCin); std::cout.rdbuf(origCout); // 6. 验证恢复 std::cout << "\n操作完成!结果已保存到result.out\n"; return 0; } ``` --- ## 临时重定向技巧 ```cpp #include
#include
struct IORedirect { std::streambuf* orig_cin; std::streambuf* orig_cout; std::ifstream in; std::ofstream out; IORedirect(const char* inFile, const char* outFile) : in(inFile), out(outFile) { orig_cin = std::cin.rdbuf(in.rdbuf()); orig_cout = std::cout.rdbuf(out.rdbuf()); } ~IORedirect() { std::cin.rdbuf(orig_cin); std::cout.rdbuf(orig_cout); } }; int main() { { // 在此作用域内所有I/O重定向到文件 IORedirect redirect("input.txt", "output.txt"); std::string name; int age; std::cout << "Enter name: "; std::cin >> name; std::cout << "Enter age: "; std::cin >> age; std::cout << "\nUser: " << name << ", Age: " << age; } // 离开作用域时自动恢复 // 现在恢复为标准I/O std::cout << "\n返回到控制台输出\n"; return 0; } ``` --- ## 注意事项 1. **错误处理**: ```cpp if (!inFile) { std::cerr << "无法打开输入文件\n"; return 1; } ``` 2. **同步问题**: - 重定向后立即生效 - 恢复后立即生效 3. **缓冲机制**: - 文件流有缓冲区,可使用 `flush()` 强制写入 ```cpp std::cout << "重要数据" << std::flush; ``` 4. **混合使用**: - 可以只重定向输入或输出中的一个 - 同时重定向时,cin 和 cout 互不影响 5. **格式兼容性**: - 文件输入输出保持与控制台相同的格式 ```cpp std::cout << std::hex << 255; // 写入 "ff" ``` --- ## 替代方案:命令行重定向 在运行程序时直接重定向: ```bash # 输入重定向 ./myprogram < input.txt # 输出重定向 ./myprogram > output.txt # 同时重定向 ./myprogram < input.txt > output.txt ``` --- ## 总结 通过流重定向技术,可以实现: - 使用 `cin` 从文件读取数据 - 使用 `cout` 向文件写入数据 - 保持原有代码逻辑不变 - 灵活切换输入输出目标 关键函数: - `rdbuf()` 获取/设置流缓冲区 - `std::streambuf` 流缓冲区基类 这种技术特别适合: - 需要切换I/O目标的场景 - 保持代码不变仅改变输入输出源 - 创建可配置的I/O系统