玄武纪8 月 · CSP-J 初赛打卡DAY 20 / 20

Day 20 文件与基本读写

建议用时:20~25 分钟
今日目标:理解标准输入输出与文件输入输出的区别,掌握 ifstreamofstream、打开模式、正确读取循环和 freopen 的基本作用。
大纲定位:入门级文件与基本读写;不展开二进制文件结构、随机访问和系统级文件接口。

一、程序关闭后,内存里的数据通常不会自动保存

程序运行时,变量主要存放在内存中;程序结束后,这些临时数据通常不再保留。文件位于外部存储中,可以让数据在程序结束后继续存在。

平时最熟悉的是标准输入输出:

cin >> x;    // 从标准输入读取,在线评测中通常由测试数据提供
cout << x;   // 写到标准输出,评测系统据此检查答案

文件输入输出则指定文件作为数据来源或去向:

ifstream fin("input.txt");
ofstream fout("output.txt");
fin >> x;
fout << x;

现在多数 OJ 题目要求使用标准输入、标准输出。只有题目明确指定文件名时才按要求读写文件;本地调试时也可以使用文件。不能把“信息学比赛一定必须文件读写”当作普遍规则。

二、三个文件流工具

使用 C++ 文件流需要包含头文件:

#include <fstream>

它们的操作习惯与 cincout 很像:

ifstream fin("data.txt");
ofstream fout("answer.txt");

int a, b;
fin >> a >> b;
fout << a + b << '\n';

若文件对象是局部变量,离开作用域时会自动关闭;显式调用 close() 仍能清楚表达“此处已经用完文件”,尤其在随后还要重新打开文件时很有用。

标准输入输出与文件流知识卡

三、打开文件后先确认是否成功

文件名写错、路径不存在或权限不足,都可能导致打开失败。可靠的程序应检查文件流状态:

ifstream fin("data.txt");
if (!fin) {
    cout << "open failed\n";
    return 0;
}

相对路径通常以程序运行时的当前工作目录为基准,不一定就是源代码所在目录。初赛如果给出“文件打不开”的情境,除了语法,还要考虑路径和权限。

四、>>getline 读取方式不同

fin >> x 会跳过前导空白,适合读取用空格或换行分隔的数字、单词:

int x;
fin >> x;

getline(fin, s) 读取一整行,可以保留行内空格,但不把行末换行符放进字符串:

string s;
getline(fin, s);

若先用 fin >> x,再紧接着 getline(fin, s),输入缓冲区里可能还留着上一行末尾的换行符,使第一次 getline 读到空行。可根据数据格式先用 fin.ignore(...) 处理残留换行。做题时要先判断程序采用哪种读取方式。

五、正确的“读到文件结束”写法

推荐把“读取成功”直接作为循环条件:

int x;
while (fin >> x) {
    // 只有成功读到一个整数,才处理 x
}

不推荐这样写:

while (!fin.eof()) {
    fin >> x;
    // 这里可能在读取失败后仍错误地使用旧的 x
}

eof() 往往要在一次读取尝试失败后才会被置为真。把读取操作放进条件中,既能处理文件结束,也能处理格式不匹配等读取失败情况。

打开、读取、写入与关闭知识卡

六、常见打开模式

打开模式可以用 | 组合:

ofstream log("log.txt", ios::app);  // 在文件末尾追加
log << "new record\n";

普通的 ofstream fout("answer.txt"); 通常会以输出方式打开,并清除原有内容后从头写入。若想保留旧内容并继续追加,要明确使用 ios::app

初赛基础题主要考模式含义。文本模式下的 <<>> 处理的是字符形式的数据;二进制模式不等于“自动把对象正确序列化”,复杂对象的二进制存储还有更多规则,本日不展开。

七、freopen 是把标准流改到文件

freopen 常用于本地调试或题目明确要求文件输入输出的场景:

#include <cstdio>

int main() {
    freopen("input.txt", "r", stdin);
    freopen("output.txt", "w", stdout);

    int a, b;
    cin >> a >> b;
    cout << a + b << '\n';
}

重定向应在读写操作之前完成。提交到普通 OJ 前,要按照题目要求决定是否保留;若题目要求标准输入输出,却把程序固定重定向到本地不存在的文件,评测时就可能读不到数据。

文件模式、读取循环与重定向知识卡

八、文件程序阅读的检查顺序

  1. 看流对象类型:ifstream 读,ofstream 写,fstream 再看模式;
  2. 看文件名与打开模式,是否覆盖或追加;
  3. 看程序有没有检查打开成功;
  4. 区分 >>getline,注意残留换行;
  5. 判断循环是否以“读取成功”为条件;
  6. 最后确认结果写到了屏幕还是某个文件。

今日选择题

近五年 CSP-J 第一轮单选中没有找到可核实的文件读写直接原题,因此今天 8 题均按初赛常见设问方式编写,不标注为真题。

  1. 使用 ifstreamofstream 前,通常需要包含的头文件是( )。

    A. <vector>  B. <fstream>  C. <cmath>  D. <queue>

  2. 下列主要用于从文件读取数据的对象定义是( )。

    A. ofstream fin("data.txt");
    B. ifstream fin("data.txt");
    C. ostream fin("data.txt");
    D. iostream fin("data.txt");

  3. 希望保留 log.txt 原有内容,并把新内容追加到文件末尾,合适的写法是( )。

    A. ofstream out("log.txt", ios::app);
    B. ifstream out("log.txt", ios::out);
    C. ofstream out("log.txt", ios::in);
    D. ofstream out("log.txt", ios::trunc);

  4. 要从文件中读取数量未知的一批整数,下列循环最稳妥的是( )。

    A. while (!fin.eof()) { fin >> x; use(x); }
    B. while (fin >> x) { use(x); }
    C. while (true) { use(x); }
    D. for (;;) { fin.eof(); use(x); }

  5. 关于 getline(fin, s),下列说法正确的是( )。

    A. 只能读取一个不含空格的单词
    B. 读取一整行,行内空格可以进入字符串,行末换行符不进入字符串
    C. 只能读取整数
    D. 每次都会把整个文件一次读完

主题程序阅读

假设 data.txt 的内容为:

3 5
2 10

阅读下面的程序:

#include <fstream>
using namespace std;

int main() {
    ifstream fin("data.txt");
    ofstream fout("result.txt");
    int x, sum = 0, cnt = 0;
    while (fin >> x) {
        sum += x;
        cnt++;
    }
    fout << cnt << ' ' << sum << '\n';
    return 0;
}
  1. while 循环的循环体一共执行( )次。

    A. 2  B. 3  C. 4  D. 5

  2. 程序结束后,result.txt 的内容是( )。

    A. 4 20  B. 4 10  C. 3 20  D. 20 4

  3. 该程序的结果写入 result.txt,不会由 cout 直接显示在标准输出中。(判断对错)

暂停 · 先完成并提交

先看流向,再算结果

请先独立完成全部题目,并到玄武 OJ 提交今日答案。

  • 标出每个文件流是读还是写;
  • 读取循环只在成功读到数据后执行;
  • 提交后再向下核对完整题目和解析。
继续向下:每道题的完整题目、答案与解析
本页用于 CSP-J 第一轮自主复习。请先看知识卡并独立完成练习,提交玄武 OJ 后再查看解析。
← 上一天 返回学习中心