freopen 函数用法教程:CSP-J 复赛文件输入输出

适用对象: A 班同学。默认你已完成第 1 课,会用 cin / cout 独立写 GESP 四级题 使用方式: 自学讲义。所有示例必须在 Dev-C++ 里亲手跑一遍,不许只看 学完本教程,你应该能:

  1. 默写 freopen 考场两行模板,说清每个参数的含义
  2. 在 Dev-C++ 里用 .in / .out 文件完成一次完整的本地测试
  3. 独立排查"文件名错误、模式写反、调试残留"这三类爆零事故
  4. 知道 freopen 的返回值有什么用、本地调试有哪些安全姿势

使用说明:

符号 含义
基础题,热身
⭐⭐ 实战题,对应复赛 T1 难度
⭐⭐⭐ 冲刺题,考场排错实战
⚠️ 考场易错点
📝 规则/结论速记

1. 为什么非得用 freopen(5 分钟)

平时刷题:键盘输入 → 屏幕输出。 CSP-J 复赛:程序自己打开文件读数据,把答案写进另一个文件,全程没有键盘和屏幕什么事。

题目名叫 apple,你的程序就必须:

评测机拿你的 apple.out 和标准答案比对(忽略行末空格和文末空行)。文件名错一个字母,评测机连你的输出都找不到——0 分,代码写对也是 0 分。

把"键盘/屏幕"切换到"文件",C++ 里最省事的办法就是一个函数:freopen

📝 freopen = 重新打开标准输入/输出,让 cin 从文件读、cout 往文件写。


2. freopen 函数详解(15 分钟)

2.1 函数原型

#include <cstdio>   // bits/stdc++.h 里已包含

FILE* freopen(const char* filename,  // 文件名
              const char* mode,      // 打开方式
              FILE* stream);         // 要重定向的流

三个参数,一个一个看:

参数 考场上写什么 含义
filename "apple.in" / "apple.out" 文件名字符串,必须带双引号,拼写和题目要求一字不差
mode "r" / "w" "r" = read 读入(给 .in 用);"w" = write 写出(给 .out 用)
stream stdin / stdout stdin = 标准输入(cin 的来源);stdout = 标准输出(cout 的去向)

排列组合一下,考场只有两种搭配:

freopen("apple.in", "r", stdin);    // 让 cin 改从 apple.in 读
freopen("apple.out", "w", stdout);  // 让 cout 改写进 apple.out

📝 记忆口诀:in 配 r 配 stdin,out 配 w 配 stdout——输入对输入,输出对输出。

📝 模式其实还有 "a"(追加,不清空原文件),但考场用不到,知道有这回事就行。

2.2 重定向之后会发生什么

#include <bits/stdc++.h>
using namespace std;

int main() {
    freopen("apple.in", "r", stdin);
    freopen("apple.out", "w", stdout);

    int a, b;
    cin >> a >> b;          // 从 apple.in 读,不从键盘读
    cout << a + b << endl;  // 写进 apple.out,屏幕上什么都看不到
    return 0;
}

关键点:freopen 只改"源头"和"去向",后面的代码一行不用动。cin、cout、scanf、printf 全部自动跟着走。

⚠️ 运行这样的程序,黑窗口(控制台)一片空白或一闪而过是正常的——输出进了文件,不在屏幕上。去 .cpp 所在的文件夹找 apple.out 看结果。

2.3 "w" 模式的小脾气

"w" 每次运行都会先把 apple.out 清空再写。所以:

⚠️ 模式写反是最惨的事故之一:

freopen("apple.in", "w", stdin);   // 灾难:apple.in 被清空,还读不到东西

2.4 返回值:可以拿来排错

freopen 成功时返回那个流,失败时返回 NULL(最常见的原因:apple.in 根本不存在)。调试期可以这样写:

if (freopen("apple.in", "r", stdin) == NULL) {
    // 走到这里说明文件没找到:查文件名、查扩展名、查文件位置
    return 1;
}

⚠️ 这段检查是调试期帮你定位问题的,正式提交写普通两行模板即可。

⚠️ freopen 失败的典型症状:黑窗口一闪而过,.out 是空的或内容乱七八糟。看到这个症状,第一反应就是去查 .in 文件名。


3. 考场标准流程(10 分钟)

以题目 apple 为例,从拿到题到交卷:

  1. 在选手目录下建文件夹 apple,代码保存为 apple/apple.cpp
  2. 开头先写好模板(先写 freopen 再写别的,养成习惯):
#include <bits/stdc++.h>
using namespace std;

int main() {
    freopen("apple.in", "r", stdin);
    freopen("apple.out", "w", stdout);

    // ===== 下面就是你的正常代码 =====

    return 0;
}
  1. 本地测试:在 apple.cpp 旁边新建 apple.in,把样例输入粘进去保存,按 F11 运行(Dev-C++ 运行时就在 .cpp 所在文件夹里找 apple.in),打开生成的 apple.out 对答案
  2. 交卷前最后 30 分钟:逐题检查 freopen 在不在、文件名对不对、调试输出删没删

⚠️ apple.in 特别容易建成 apple.in.txt:Windows 默认隐藏扩展名。在资源管理器里勾选 查看 → 文件扩展名,确认文件名真的是 apple.in

📝 题目要求什么文件名就写什么文件名,一字不差。CSP-J 复赛惯例是全小写,但以题目 PDF 为准


4. 本地调试的三种姿势(10 分钟)

姿势一:注释法(最快,也最容易出事)

//freopen("apple.in", "r", stdin);
//freopen("apple.out", "w", stdout);

调试时注释掉,用键盘屏幕;交卷前取消注释

⚠️ 每年都有人忘了取消注释就交卷——0 分。用这个方法,就在草稿纸上写一行大字:"交卷前恢复 freopen!"

姿势二:全程文件法(最稳,推荐)

freopen 从头到尾不注释。想换测试数据就改 apple.in 的内容,运行后看 apple.out

和考场环境一模一样,不存在"忘了恢复"的问题。代价是没有交互输入,调试信息只能往 apple.out 里打(交卷前记得删)。

姿势三:条件编译法(刷网站题专用)

#ifndef ONLINE_JUDGE
    freopen("apple.in", "r", stdin);
    freopen("apple.out", "w", stdout);
#endif

很多评测网站(如洛谷)编译时会定义 ONLINE_JUDGE 这个宏:在网站评测时 freopen 自动消失,在你自己电脑上测试时 freopen 生效。一份代码两头用,不用来回注释。

⚠️ CSP-J 考场不这么写。考场要求文件输入输出,直接写死两行模板,别玩花样。


5. 爆零清单:逐条自查(5 分钟)

考前把这张表过一遍,每条都值 100 分:

# 事故 后果 自查动作
1 文件名拼错/大小写错:Apple.inaple.in 0 分 对照题目 PDF 逐字母核对
2 建成了 apple.in.txt 本地测试读不到数据 资源管理器打开扩展名显示
3 调试时注释了 freopen,交卷忘恢复 0 分 全文搜索 //freopen
4 "r""w" 写反 .in 被清空 口诀:in-r-stdin,out-w-stdout
5 往 stdout 打了调试信息("debug"、中间结果) 答案错误 全文搜索 cout,逐个确认
6 结尾写 system("pause")getchar() 等输入 超时 直接 return 0 结束
7 代码没放进题目要求的文件夹,或文件夹名错 0 分 对照考场要求检查路径
8 在网站提交时带着 freopen 0 分或 WA 网站提交前注释掉(或用姿势三)

6. 练习题

⭐ 练习 1:默写 + 改错

不看讲义,默写 freopen 考场两行模板。然后指出下面每一行错在哪:

freopen(apple.in, "r", stdin);              // (a)
freopen("apple.out", "r", stdout);          // (b)
freopen("apple.in", "w", stdin);            // (c)
freopen("apple.in", "r", stdout);           // (d)

⭐⭐ 练习 2:完整走一遍流程

题目名 plus:从 plus.in 读入两个整数(一行,空格隔开),把它们的和写进 plus.out

样例输入(plus.in):

3 5

样例输出(plus.out):

8

要求:建 plus 文件夹、写 plus.cpp、建 plus.in 测样例,全程不许看屏幕输出。再自己编两组数据测一遍(比如 100 200-7 7)。

⭐⭐⭐ 练习 3:考场找茬

下面是小明为题目 apple 写的代码,共有 5 处会导致爆零或挂分的问题,全部找出来:

#include <bits/stdc++.h>
using namespace std;

int main() {
    freopen("Apple.in", "r", stdin);
    freopen("apple.out", "r", stdout);

    int n, s = 0, x;
    cin >> n;
    cout << "n = " << n << endl;   // 调试看看读对没有
    for (int i = 1; i <= n; i++) {
        cin >> x;
        s += x;
    }
    cout << s << endl;
    system("pause");
    return 0;
}

(补充线索:小明的代码保存为 apple.cpp,但他的输入文件叫 apple.in.txt。)


7. 参考答案

练习 1

模板:

freopen("apple.in", "r", stdin);
freopen("apple.out", "w", stdout);

练习 2

#include <bits/stdc++.h>
using namespace std;

int main() {
    freopen("plus.in", "r", stdin);
    freopen("plus.out", "w", stdout);

    int a, b;
    cin >> a >> b;
    cout << a + b << endl;
    return 0;
}

plus.in 里写 3 5,运行后 plus.out 里应该是 8。换成 100 200 应得 300-7 7 应得 0全程屏幕空白是正常的,答案在文件里。

练习 3

  1. "Apple.in" 大写 A:题目要求 apple.in,文件名必须一字不差
  2. 输出文件用了 "r" 模式:应为 freopen("apple.out", "w", stdout),否则答案写不进去
  3. 调试输出 cout << "n = " << n << endl; 没删:这行会写进 apple.out,答案直接判错
  4. system("pause"):程序卡在等按键,评测超时
  5. 输入文件是 apple.in.txt:freopen 找不到 apple.in,读入失败——资源管理器打开"显示文件扩展名"就能发现

8. 速记卡片

📝 函数原型freopen("文件名", "模式", 流);

📝 考场两行

freopen("题目名.in", "r", stdin);
freopen("题目名.out", "w", stdout);

📝 口诀:in 配 r 配 stdin,out 配 w 配 stdout。

📝 三个铁律

  1. 文件名对照题目逐字母核对
  2. 调试输出交卷前删干净
  3. 不写 system("pause")