信奥竞赛

系列

剑指信奥 | C 语言之玩转文件

文件的操作

上一次课中,我们谈到了计算机中的文件,讲了文件的含义,作用和分类。

最后我们了解了文件的操作,再来看一下文件常用的操作过程:

  • 创建一份新的文件
  • 向文件中写入信息
  • 读取文件中的信息
  • 关闭已打开的文件

以上这些处理文件的过程,在计算机语言中,统称为文件操作

而对于我们关注的信息学竞赛而言,文件操作也是一个必须要学会的内容,为什么这么说?

现在,我们来看一道信息学竞赛的真题。

典型的信奥试题

为什么文件操作很重要?

我们来看一份 2019 年信息学竞赛的试题:

数字游戏 — 2019 CCF CSP-J2 第一题

问题描述

小 K 同学向小 P 同学发送了一个长度为 8 的 01 字符串来玩数字游戏,小 P 同学想要知道字符串中究竟有多少个 1。

注意:01 字符串为每一个字符是 0 或者 1 的字符串,如 “101”(不含双引号)为一个长度为 3 的 01 字符串。

输入格式

输入文件名为 number.in。输入文件只有一行,一个长度为 8 的 01 字符串 s。

输出格式

输出文件名为 number.out输出文件只有一行,包含一个整数,即 01 字符串中字符 1 的个数。

输入输出样例 1
number.in number.out
00010100 2

见选手目录下的 number/number1.in 和 number/number1.ans

输入输出样例 1 说明

该 01 字符串中有 2 个字符 1

输入输出样例 2
number.in number.out
11111111 8

见选手目录下的 number/number2.in 和 number/number2.ans

输入输出样例 2 说明

该 01 字符串中有 8 个字符 1

输入输出样例 3

见选手目录下的 number/number3.in 和 number/number3.ans

数据规模与约定

对于 20% 的数据,保证输入的字符全部为 0。对于 100% 的数据,输入只可能包含字符 0 和字符 1,字符串长度固定为 8。

这是这场考试中最简单的一道题,就是所谓的送分题。

不难发现,这道题中含有很多的文件操作,所以,对于参加竞赛的同学们,一定要熟练掌握常用的文件操作。

C 的文件操作

那么,在 C 语言中,怎样对文件进行操作呢?

以下我们逐一介绍 C 语言操作文件的方法,并以最简的代码做示例。

首先,我们在开始进行文件操作时,需要定义一个文件类型的指针。


FILE *fp;
                

指针,是一个 C 语言的一个进阶话题,大家目前只需要记住这个形式就可以,现在不必深究。

有了这个文件指针的定义,我们就可以开始进行各种文件操作了。

我们按照以下顺序,举例说明 C 语言文件操作的基本方法。

1. 打开文件
  • fopen,创建一个新文件或打开已有的文件
  • FILE,由 fopen 初始化的一个文件对象
  • 代码示例:
#include <stdio.h>
int main() {
    // 打开文件 number.in,以读取的方式打开
    FILE *fp = fopen("../number/number.in", "r");
}
                

其中,第一个参数是文件名字符串,而第二个参数则是访问文件的模式。

各种模式的含义如下表:

模式 描述
r 打开一个文本文件,允许读取文件。
w
a

打开一个文本文件,允许写入文件。

不存在,则会创建一个新文件。

程序会从文件的开头写入内容。

文件存在,则会重新写入。

r+ 打开一个文本文件,允许读写文件。
w+

打开一个文本文件,允许读写文件。

已存在,则文件会被截断为零长度。

文件不存在,则会创建一个新文件。

a+

打开一个文本文件,允许读写文件。

如果文件不存在,则创建一个新文件。

读取从头开始,写入是追加模式。

虽然看起来很复杂,其实仔细分析不难发现,最基本的模式只有 r / w / a 三种,它们的含义分别是 read / write / append,就是 读 / 写 / 追加

2. 关闭文件
  • fclose
  • 成功关闭,返回 0
  • 关闭失败,返回 EOF
  • 代码示例:
#include <stdio.h>

int main() {
    FILE *fp = fopen("../number/number1.in", "r");
    // 关闭打开的文件
    fclose(fp);
}
                
3. 写入文件
  • fputc,向文件中写入一个字符,成功返回字符
  • fputs,向文件中写一个字符串,成功返回非负值
  • 写入失败,返回 EOF
  • 代码示例:
#include <stdio.h>

int main() {
    // 创建文件 number.out,准备写文件
    FILE *fp = fopen("../number/number.out", "w");
    // 向文件中写入字符 8
    fputc('8', fp);
    fclose(fp);
}
                
4. 读取文件
  • fgetc,从文件读取单个字符
  • fgets,从文件中读取一个字符串
  • 代码示例:
#include <stdio.h>

int main() {
    // 打开文件 number.in,准备读文件
    // 文件内容:00010100
    FILE *fp = fopen("../number/number.in", "r");
    // 读取文件的第一个字符
    char c = fgetc(fp);
    // 输出读取的字符 c
    printf("%c", c); // 输出结果:8
    fclose(fp);
}
                

至此,我们就了解了 C 语言关于文件操作最常用的方法。

一份参考的题解

最后,我们利用今天学到的文件操作的方法,把这道信奥竞赛的试题完成。

通过分析,我们发现这道题解题的步骤是这样的:

  1. 以读取的方式,打开文件 number.in
  2. 通过循环的方式,读取 number.in 中的每一个字符
  3. 对每一个读取的字符判断是否等于 1,计数
  4. 循环结果后,将计数结果写入文件 number.out

现在,我们分步骤解题。

首先,创建文件 number.in:

10010010

代码 1
#include <stdio.h>

int main() {
    // 以 r 方式打开文件 number.in
    FILE *fp = fopen("../number/number.in", "r");
    // 关闭文件
    fclose(fp);
}
                
代码 2
#include <stdio.h>

int main() {
    FILE *fp = fopen("../number/number.in", "r");
    char c;
    // 循环读取 number.in 中的每一个字符
    while((c = fgetc(fp)) != EOF) {
        // 输出当前字符,测试
        printf("%c", c); // 输出:10010010
    }
    fclose(fp);
}
                
代码 3
#include <stdio.h>

int main() {
    FILE *fp = fopen("../number/number.in", "r");
    // 定义计数器 sum,赋初始值 0
    int sum = 0;
    char c;
    while((c = fgetc(fp)) != EOF) {
        // 判断当前字符是否是 1
        if (c == '1') {
            sum++; // 计数器加 1
        }
    }
    // 输出 sum,测试
    printf("%d", sum); // 输出:3
    fclose(fp);
}
                
代码 4
#include <stdio.h>

int main() {
FILE *fp = fopen("../number/number.in", "r");
int sum = 0;
char c;
while((c = fgetc(fp)) != EOF) {
    if (c == '1') {
        sum++;
    }
}
fclose(fp);
// 以 w 方式创建文件 number.out
fp = fopen("../number/number.out", "w");
// 把计数结果写入文件 number.out
fputc(sum + '0', fp);
fclose(fp);
}
                

至此,我们就完成了这道信奥试题,不足 20 行代码,如果熟练的话,大概用时 5 分钟。

今天的内容有点多,同学们一定要好好复习一下哦,我们下次课见!

日积月累

  • open 打开
  • close 关闭
  • read 读
  • write 写
  • append 追加
  • EOF 文件结尾(End Of File )