




















在前面的文章中,我们介绍了 C++ Standard Template Library (STL) 中的通用算法库 <algorithm>。掌握了这些常用算法后,我们已经能够写出非常高效的数据处理代码。
然而,在实际参加信奥竞赛(如 CSP-J/S、NOIP 等)时,仅仅写出逻辑正确的代码是不够的。信奥比赛有一条极其严格的硬性规定:程序必须通过文件进行数据的输入与输出。
如果你没有按照要求进行文件操作,或者把文件名拼写错了哪怕一个字母,测评机都将无法读取你的输出,最终导致该题直接判定为 0 分(俗称“爆零”)!
今天,我们就来学习如何使用 C++ 中最简单、最常用的文件输入输出重定向函数 —— std::freopen,为你的比赛代码保驾护航。
往期回顾:
在我们平时的编程练习中,通常是在控制台(终端)中输入数据,然后直接在屏幕上查看输出结果。这被称为 标准输入输出(Standard I/O):
stdin):默认是键盘。stdout):默认是屏幕。但是在信奥比赛中,成千上万名选手的代码需要提交到测评系统进行自动阅卷。如果每个人都需要手动输入测试数据,测评系统将无法工作。因此,比赛采用文件读写的方式:
sum.in);sum.out);要在 C++ 中实现这一点,最轻松、改动最小的方法就是使用 输入输出重定向。
std::freopenstd::freopen 定义在头文件 <cstdio> 中。它的作用是重新定向标准输入输出流。
1
std::freopen(const char* filename, const char* mode, std::FILE* stream);
对于信奥选手,我们只需要记住以下两行固定搭配:
1
2
3
4
5
// 1. 将标准输入重定向到指定文件(只读模式 "r")
std::freopen("problem.in", "r", stdin);
// 2. 将标准输出重定向到指定文件(只写模式 "w")
std::freopen("problem.out", "w", stdout);
filename:目标文件名(如 "problem.in"、"problem.out")。必须与比赛题目要求的英文名称完全一致。mode:文件打开模式。"r" 代表只读(Read),"w" 代表只写(Write,若文件已存在则会覆盖)。stream:要重定向的流。stdin 代表标准输入,stdout 代表标准输出。[!NOTE] 使用
freopen的最大好处在于:你的核心代码不需要做任何修改。你依然可以使用熟悉的std::cin/std::cout或者std::scanf/std::printf进行数据读写,系统会自动帮你在后台完成文件对接。
我们以最基础的“两数求和”为例,要求从 aplusb.in 中读取两个整数,并将它们的和输出到 aplusb.out 中。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
#include <cstdio> // 必须引入此头文件以使用 freopen
int main() {
// 1. 在 main 函数的最开始加入重定向
std::freopen("aplusb.in", "r", stdin);
std::freopen("aplusb.out", "w", stdout);
// 2. 正常编写你的业务逻辑代码
int a, b;
if (std::cin >> a >> b) {
std::cout << a + b << std::endl;
}
// 3. (可选但推荐) 规范地关闭文件指针
std::fclose(stdin);
std::fclose(stdout);
return 0;
}
如果你直接在集成开发环境(IDE,如 Dev-C++、Code::Blocks 或 VS Code)中点击“运行”:
aplusb.in。aplusb.in,程序读取不到数据,控制台可能直接闪退或结束。.exe 文件的同级目录下,多出了一个名叫 aplusb.out 的空文件。aplusb.in;aplusb.in,输入两个数字(例如 12 34),保存并关闭;aplusb.out 文件,你会发现里面已经写好了答案 46。在写代码时,为了方便调试,我们通常希望在本地运行时能通过屏幕 and 键盘交互,而提交给测评机时又自动使用文件重定向。
这里介绍两种常用的处理方法:
在写代码和本地调试时,将 freopen 的行注释掉。当确认代码逻辑无误、准备提交前,务必解开注释:
1
2
3
4
5
6
7
8
9
10
int main() {
// 本地调试时注释掉下面两行,手动在控制台输入数据
// std::freopen("sum.in", "r", stdin);
// std::freopen("sum.out", "w", stdout);
int a, b;
std::cin >> a >> b;
std::cout << a + b << std::endl;
return 0;
}
[!WARNING] 这种方法虽然稳妥,但最怕的就是交卷前忘记解开注释。忘记写或忘记解开
freopen的注释,通常会导致整道题直接得 0 分。
我们可以利用预处理器指令 #ifndef(If Not Defined)来自动控制。在大多数 Online Judge(如洛谷、POJ、CF等)上,测评系统会默认定义一个名为 ONLINE_JUDGE 的宏,而选手的本地电脑上没有定义这个宏。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
#include <cstdio>
int main() {
// 如果不是在线评测,就执行文件重定向(即本地调试时读取文件)
#ifndef ONLINE_JUDGE
std::freopen("sum.in", "r", stdin);
std::freopen("sum.out", "w", stdout);
#endif
int a, b;
std::cin >> a >> b;
std::cout << a + b << std::endl;
return 0;
}
ONLINE_JUDGE,重定向生效,程序读取 sum.in 并写入 sum.out。ONLINE_JUDGE,这部分代码被自动忽略,程序依然使用标准输入输出。[!IMPORTANT] 特别提醒:对于 CSP-J/S、NOIP 等线下复赛,评测是在官方提供的离线测试环境(测评机)中进行的,官方评测机不一定会定义
ONLINE_JUDGE!因此,在 CSP-J/S 复赛中,最稳妥的做法是:绝对不要使用条件编译来提交。必须在提交最终代码前,老老实实解开
freopen的注释,确保那两行重定向代码直接暴露在外面!
根据历届信奥比赛的数据统计,每年都有大量选手因为文件读写错误而惨遭“爆零”。请大家务必牢记以下几点:
文件名多一个字母、少一个字母、大小写写错,均会导致系统找不到文件。
apple.in,你写成了 aple.in,或者写成了大写的 Apple.in。freopen 中。在 Windows 电脑上,默认会隐藏文件后缀名。如果你新建一个文本文件并命名为 sum.in,由于隐藏了后缀,它的真实名字其实是 sum.in.txt!
sum.in 从而报错。在 freopen 中,只写文件名即可,例如 "apple.in",这表示相对路径(在当前程序运行的文件夹中寻找)。
"D:\\csp\\apple.in"),因为评测系统上的目录结构与你的个人电脑完全不同,写绝对路径会导致评测机找不到文件。在前面的文章中我们提到,可以使用以下代码加速 std::cin 和 std::cout 的效率:
1
2
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
这两行加速语句与 freopen 并不冲突,两者可以同时使用。通常把加速语句写在 freopen 的下方即可。
本文介绍了 C++ 竞赛中至关重要的文件重定向操作 freopen。文件操作是信奥竞赛的入场券,不容许出现一丝一毫的马虎。希望大家在平时的练习中就养成规范书写文件读写的习惯。
下一篇文章中,我们将详细讲解 【衡量代码效率的标尺——时间复杂度与空间复杂度(以实例说话)】,带大家学会如何估算代码的运行时间与内存,避免在竞赛中超时失分。
所有代码已上传至Github:https://github.com/lihongzheshuai/yummy-code
GESP 学习专题站:GESP WIKI
"luogu-"系列题目可在洛谷题库进行在线评测。
"bcqm-"系列题目可在编程启蒙题库进行在线评测。
欢迎加入:Java、C++、Python技术交流QQ群(982860385),大佬免费带队,有问必答
欢迎加入:C++ GESP/CSP认证学习QQ频道,考试资源总结汇总
欢迎加入:C++ GESP/CSP学习交流QQ群(688906745),考试认证学员交流,互帮互助
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。