本篇整理了 C 语言文件操作的课堂代码,涵盖文件打开与关闭、文件读写操作,以及一道填空题的题目与答案对比解析。
目录
一、文件打开与关闭:读取文件内容
题目说明
阅读并理解一个最基本的文件读取程序:打开文件 1.txt,逐字符读取其中的内容并输出到屏幕,最后关闭文件。
解题思路
- 定义文件指针
FILE *fp,所有文件操作都通过该指针进行。 - 用
fopen(filename, "r")以”只读”方式打开文件,若打开失败返回NULL。 - 检查打开是否成功:
if ((fp = fopen(...)) == NULL),失败则提示并退出。 - 用
fgetc(fp)逐字符读取,直到返回EOF(文件结束标志)。 - 用
putchar(ch)将读取的字符输出到屏幕。 - 最后用
fclose(fp)关闭文件,释放资源。
完整代码
#include <stdio.h>int main(){ FILE *fp; /* 定义文件指针 */ char ch, *filename = "1.txt"; /* 文件名 */ /* 以只读方式打开文件,并检查是否成功 */ if ((fp = fopen(filename, "r")) == NULL) { printf("cannot open file\n"); exit(0); /* 打开失败,退出程序 */ } /* 逐字符读取文件内容直到EOF(文件结束) */ while ((ch = fgetc(fp)) != EOF) putchar(ch); /* 输出读取的字符到屏幕 */ fclose(fp); /* 关闭文件 */ return 0;}关键知识点
FILE类型:C 标准库定义的文件结构体类型,FILE *是文件指针,所有文件 I/O 函数都需它作为参数。fopen函数:FILE *fopen(const char *filename, const char *mode);- 模式
"r":只读,文件必须存在,否则返回NULL。 - 模式
"w":只写,文件不存在则创建,存在则清空内容。 - 模式
"a":追加,文件不存在则创建,存在则在其末尾追加。 - 还有
"r+"、"w+"、"a+"等读写组合模式。
- 模式
fgetc函数:int fgetc(FILE *fp);从文件读取一个字符,返回int(不是char),读到文件末尾返回EOF(通常为 -1)。EOF:End Of File,文件结束标志宏,定义为 -1。这也是为什么fgetc返回int而非char——若返回char,则 0xFF 会被误判为EOF。fclose函数:关闭文件,刷新缓冲区,释放系统资源。打开的文件务必关闭,否则可能导致数据丢失或资源泄漏。- 打开失败检查的必要性:文件可能不存在、无权限或路径错误,必须检查
fopen返回值是否为NULL。
小贴士:本程序使用了
exit(0)但未包含<stdlib.h>,严格来说应包含该头文件。exit函数声明在<stdlib.h>中。
二、文件读写操作:从键盘写入文件
题目说明
从键盘输入字符,逐个存到磁盘文件中,直到输入 # 为止。同时将输入的字符回显到屏幕上。
解题思路
- 定义文件指针
FILE *fp和字符变量ch、文件名数组filename[20]。 - 提示用户输入文件名,用
scanf("%s", filename)读入。 - 用
getchar()吸收scanf留下的回车符,避免它被当作第一个输入字符。 - 以
"w"(只写)方式打开文件,检查是否成功。 - 循环读取键盘输入
ch = getchar(),当ch != '#'时:- 用
fputc(ch, fp)将字符写入文件。 - 用
putchar(ch)将字符回显到屏幕。
- 用
- 输入
#后结束循环,fclose(fp)关闭文件。
完整代码
/*从键盘输入字符,逐个存到磁盘文件2.txt中,直到输入'#'为止*/#include <stdio.h>#include <stdlib.h>int main(){ FILE* fp; /* 步骤1:定义文件指针 */ char ch, filename[20]; printf("Please input(file name):"); /* 输入写入数据的文件名 */ scanf("%s", filename); getchar(); /* 接收执行scanf函数时,最后的回车 */ if ((fp = fopen(filename, "w")) == NULL) /* 步骤2:文件打开(只写模式) */ { printf("Can not open file.\n"); exit(0); }
printf("Please input(end of #):"); ch = getchar(); /* 接收输入的第一个字符 */ while (ch != '#') { fputc(ch, fp); /* 步骤3:文件写操作,将字符ch输出到fp所指向的文件中 */ putchar(ch); /* 将字符ch输出在显示屏上 */ ch = getchar(); /* 逐个接收从键盘输入的字符 */ } putchar('\n'); fclose(fp); /* 步骤4:文件关闭 */ return 0;}关键知识点
fputc函数:int fputc(int ch, FILE *fp);将字符ch写入文件fp,写入成功返回该字符,失败返回EOF。getchar吸收回车:scanf("%s", ...)读取字符串后会留下回车符在缓冲区,若不吸收,下一次getchar()会读到回车而非实际输入。这是 C 语言输入常见的”坑”。- 文件写模式
"w":若文件已存在,会清空原内容;若不存在,则创建新文件。要保留原内容应在末尾追加,使用"a"模式。 - 键盘输入与文件写入的同步:用户输入的字符既写入文件又显示在屏幕上,便于用户确认输入内容。
- 结束标志:用
#作为输入结束标志,是 C 语言入门程序常见的约定。
三、填空题解析:文件写入完整流程
本节是一道文件操作填空题,给出了题目版本(含空待填)和答案版本(完整代码),通过对比帮助理解填空点。
题目版本(含空待填)
/*从键盘输入字符,逐个存到磁盘文件2.txt中,直到输入'#'为止*/#include <stdio.h>#include <stdlib.h>int main( ){ FILE* fp ; /* 步骤1:定义文件指针 */ char ch , filename[20] ; printf( "Please input(file name):" ) ; /* 输入写入数据的文件名 */ scanf( "%s" , filename ) ; getchar( ) ; /* 接收执行scanf函数时,最后的回车 */ if( fp == NULL) /* 步骤2:文件打开 */ <--- 此处需填空 { printf( "Can not open file.\n" ) ; exit( 0 ) ; }
printf( "Please input(end of #):" ) ; ch = getchar( ) ; /* 接收输入的第一个字符 */ while( ch != '#' ) { fputc(ch, fp); /* 步骤3:文件写操作,将字符ch输出到fp所指向的文件中 */ putchar( ch ) ; /* 将字符ch输出在显示屏上 */ ch = getchar( ) ; /* 逐个接收从键盘输入的字符 */ } putchar( '\n' ) ; fclose(fp); /* 步骤4.文件关闭 */ return 0 ;}题目分析
题目要求实现”从键盘输入字符写入文件”的功能,但 if 语句的条件部分被挖空了:if( fp == NULL)。注意这里 fp 是文件指针,尚未通过 fopen 赋值,直接与 NULL 比较是错误的——fp 此刻是未初始化的野指针,比较结果无意义。
需要填入的是 fopen 调用,并让整个表达式与 NULL 比较,以判断打开是否失败。
答案版本(完整代码)
/*从键盘输入字符,逐个存到磁盘文件2.txt中,直到输入'#'为止*/#include <stdio.h>#include <stdlib.h>int main(){ FILE* fp; /* 步骤1:定义文件指针 */ char ch, filename[20]; printf("Please input(file name):"); /* 输入写入数据的文件名 */ scanf("%s", filename); getchar(); /* 接收执行scanf函数时,最后的回车 */ if ((fp = fopen(filename, "w")) == NULL) /* 步骤2:文件打开(答案填空处) */ { printf("Can not open file.\n"); exit(0); }
printf("Please input(end of #):"); ch = getchar(); /* 接收输入的第一个字符 */ while (ch != '#') { fputc(ch, fp); /* 步骤3:文件写操作,将字符ch输出到fp所指向的文件中 */ putchar(ch); /* 将字符ch输出在显示屏上 */ ch = getchar(); /* 逐个接收从键盘输入的字符 */ } putchar('\n'); fclose(fp); /* 步骤4.文件关闭 */ return 0;}题目与答案对比分析
填空点定位
| 位置 | 题目版本 | 答案版本 | 说明 |
|---|---|---|---|
if 条件 | if( fp == NULL) | if ((fp = fopen(filename, "w")) == NULL) | 核心填空点 |
填空内容详解
填入的内容是 fp = fopen(filename, "w"),整个 if 条件变为:
if ((fp = fopen(filename, "w")) == NULL)这一行完成了两件事:
- 赋值:
fp = fopen(filename, "w")调用fopen打开文件,将返回的文件指针赋给fp。 - 判断:将赋值后的
fp与NULL比较,判断打开是否失败。
为什么要用”赋值 + 比较”的写法
这是 C 语言中检查 fopen 的惯用写法,原因如下:
fopen可能失败(文件不存在、无权限、磁盘满等),失败时返回NULL。- 若不检查直接使用
fp,程序会因解引用NULL指针而崩溃。 - 把”赋值”和”判断”合并到一行,既简洁又不易遗漏检查。
等价的展开写法是:
fp = fopen(filename, "w");if (fp == NULL){ printf("Can not open file.\n"); exit(0);}两种写法功能完全等价,但合并写法更紧凑,是 C 程序员的习惯写法。
注意运算符优先级
- 赋值运算符
=的优先级低于关系运算符==,所以必须用括号把fp = fopen(...)括起来。 - 若写成
if (fp = fopen(filename, "w") == NULL)(缺少外层括号),由于==优先级高,会先计算fopen(...) == NULL得到 0 或 1,再赋给fp,导致fp变成 0 或 1 而非有效的文件指针——这是一个经典的优先级 bug。
易错点小结
- 忘记
fopen:直接用未初始化的fp比较NULL,逻辑错误。 - 括号缺失:
fp = fopen(...) == NULL因优先级问题导致fp被赋为 0 或 1。 - 模式字符串错误:本题是”写入”文件,必须用
"w"而非"r"。若用"r",文件不存在时会打开失败。 - 忘记
getchar()吸收回车:scanf后回车残留在缓冲区,会导致第一个getchar()读到回车而非用户实际输入。 - 忘记
fclose:写入的数据可能还在缓冲区未刷盘,程序结束后数据丢失。
四、文件操作核心知识点总结
文件操作的四个标准步骤
所有文件 I/O 程序都遵循以下四步流程:
定义文件指针 → 打开文件 → 读写文件 → 关闭文件 FILE *fp fopen() fgetc/fputc fclose() fgets/fputs fread/fwrite fprintf/fscanf常用文件函数对照表
| 函数 | 功能 | 函数原型 | 说明 |
|---|---|---|---|
fopen | 打开文件 | FILE *fopen(const char *name, const char *mode); | 失败返回 NULL |
fclose | 关闭文件 | int fclose(FILE *fp); | 成功返回 0,失败返回 EOF |
fgetc | 读一个字符 | int fgetc(FILE *fp); | 返回 int,末尾返回 EOF |
fputc | 写一个字符 | int fputc(int ch, FILE *fp); | 成功返回 ch,失败返回 EOF |
fgets | 读一行字符串 | char *fgets(char *s, int n, FILE *fp); | 读 n-1 个字符或遇换行 |
fputs | 写字符串 | int fputs(const char *s, FILE *fp); | 不自动加换行 |
fscanf | 格式化读 | int fscanf(FILE *fp, const char *fmt, ...); | 类似 scanf |
fprintf | 格式化写 | int fprintf(FILE *fp, const char *fmt, ...); | 类似 printf |
fread | 读数据块 | size_t fread(void *buf, size_t size, size_t n, FILE *fp); | 二进制读 |
fwrite | 写数据块 | size_t fwrite(const void *buf, size_t size, size_t n, FILE *fp); | 二进制写 |
feof | 判断文件结束 | int feof(FILE *fp); | 结束返回非 0 |
文件打开模式对照表
| 模式 | 含义 | 文件不存在时 | 文件存在时 |
|---|---|---|---|
"r" | 只读 | 失败 | 从开头读 |
"w" | 只写 | 创建 | 清空内容 |
"a" | 追加 | 创建 | 在末尾追加 |
"r+" | 读写 | 失败 | 从开头读写 |
"w+" | 读写 | 创建 | 清空内容 |
"a+" | 读追加 | 创建 | 可读,写时追加 |
"rb" | 二进制只读 | 失败 | 从开头读 |
"wb" | 二进制只写 | 创建 | 清空内容 |
注意:
"w"和"w+"模式会清空原文件内容,使用前务必确认!若想保留原内容,应使用"a"或"a+"。
文本文件 vs 二进制文件
| 特性 | 文本文件 | 二进制文件 |
|---|---|---|
| 存储方式 | ASCII 字符 | 内存原样存储 |
| 读写函数 | fgetc/fputc/fgets/fputs/fscanf/fprintf | fread/fwrite |
| 模式后缀 | 无 b(如 "r") | 带 b(如 "rb") |
| 换行处理 | \n 与 \r\n 转换 | 不转换 |
| 可读性 | 文本编辑器可读 | 需专用程序 |
| 存储效率 | 较低(数字占多个字符) | 较高(数字按类型定长) |
编程规范建议
- 始终检查
fopen返回值:if ((fp = fopen(...)) == NULL)是标准写法。 - 打开的文件务必关闭:每个
fopen对应一个fclose,建议成对编写。 - 二进制文件用
"b"模式:跨平台时避免换行符转换问题。 - 缓冲区刷新:
fflush(fp)可手动刷新缓冲区;正常fclose也会刷新。 - 文件结束判断:用
feof或检查fgetc是否返回EOF,避免死循环。 - 错误处理:
ferror(fp)可检测读写错误,clearerr(fp)清除错误标志。
掌握以上文件操作基础后,可以进一步学习:文件指针定位(fseek/ftell/rewind)、临时文件(tmpfile)、文件删除重命名(remove/rename)等进阶内容。
如果这篇文章对你有帮助,欢迎分享给更多人!
部分信息可能已经过时












