C语言文件操作:数据持久化的实现
《C语言文件操作:数据持久化的实现》

一、前言:为什么需要文件操作?
学习目标
- 理解文件操作的本质:将数据从内存保存到磁盘(持久化) 或 从磁盘读取数据到内存
- 明确文件操作的作用:保存程序的运行结果、读取外部数据、处理大型数据
- 掌握本章学习重点:文件操作的基本概念、文件打开与关闭、文件读写操作、文件定位、二进制文件操作
重点提示
💡 文件操作是C语言程序开发的重要环节!它让你的程序能够处理大量数据,并将结果永久保存,而不必每次运行时都重新输入数据。
二、模块1:文件操作的基本概念——了解文件的本质
2.1 学习目标
- 理解文件的本质:磁盘上的数据集合,具有唯一的文件名和路径
- 掌握文件的分类:文本文件、二进制文件
- 理解文件指针的概念:FILE类型的指针变量,用于访问文件
- 学会使用stdio.h头文件中的常用宏定义
2.2 文件的分类
2.2.1 文本文件
定义:文本文件是以字符编码形式(如ASCII、UTF-8)保存的文件,内容可以通过文本编辑器(如记事本)查看
特点:
- 内容可读性强
- 占用空间较大
- 适合保存纯文本数据(如文档、代码、日志等)
代码示例1:创建一个简单的文本文件
#include <stdio.h>
int main() {
// 使用记事本创建一个文本文件test.txt,内容为:Hello, World!
return 0;
}
2.2.2 二进制文件
定义:二进制文件是以字节形式保存的文件,内容不能通过文本编辑器直接查看,需要特定的程序来解析
特点:
- 内容可读性差
- 占用空间较小
- 适合保存二进制数据(如图片、音频、视频、编译后的程序等)
代码示例2:创建一个简单的二进制文件
#include <stdio.h>
int main() {
// 使用代码创建一个二进制文件
FILE *fp = fopen("data.bin", "wb");
if (fp == NULL) {
printf("文件打开失败!\n");
return 1;
}
int numbers[] = {10, 20, 30, 40, 50};
fwrite(numbers, sizeof(int), 5, fp);
fclose(fp);
return 0;
}
2.3 文件指针
定义:文件指针是FILE类型的指针变量,用于访问文件。FILE类型是C语言标准库中定义的一个结构体类型,包含了文件的相关信息(如文件位置、打开模式等)
代码示例3:文件指针的声明
#include <stdio.h>
int main() {
FILE *fp; // 声明一个文件指针
return 0;
}
三、模块2:文件打开与关闭——访问文件的基础
3.1 学习目标
- 掌握文件打开函数
fopen的用法 - 学会使用不同的打开模式
- 掌握文件关闭函数
fclose的用法 - 避开文件打开与关闭的3大常见坑
3.2 文件打开函数fopen
语法:
FILE *fopen(const char *filename, const char *mode);
参数:
-
filename:文件名或完整路径 -
mode:打开模式(只读、只写、追加等)
返回值:成功返回文件指针,失败返回NULL
常用打开模式:
| 模式 | 含义 |
|---|---|
| “r” | 只读方式打开文本文件(文件必须存在) |
| “w” | 只写方式打开文本文件(文件不存在则创建,存在则清空) |
| “a” | 追加方式打开文本文件(文件不存在则创建,写入时追加到文件末尾) |
| “r+” | 读写方式打开文本文件(文件必须存在) |
| “w+” | 读写方式打开文本文件(文件不存在则创建,存在则清空) |
| “a+” | 读写方式打开文本文件(文件不存在则创建,写入时追加到文件末尾) |
| “rb” | 只读方式打开二进制文件(文件必须存在) |
| “wb” | 只写方式打开二进制文件(文件不存在则创建,存在则清空) |
| “ab” | 追加方式打开二进制文件(文件不存在则创建,写入时追加到文件末尾) |
| “rb+” | 读写方式打开二进制文件(文件必须存在) |
| “wb+” | 读写方式打开二进制文件(文件不存在则创建,存在则清空) |
| “ab+” | 读写方式打开二进制文件(文件不存在则创建,写入时追加到文件末尾) |
代码示例4:使用fopen函数打开文件
#include <stdio.h>
int main() {
FILE *fp = fopen("test.txt", "r");
if (fp == NULL) {
printf("文件打开失败!\n");
return 1;
}
printf("文件打开成功!\n");
fclose(fp);
return 0;
}
3.3 文件关闭函数fclose
语法:
int fclose(FILE *fp);
参数:
-
fp:要关闭的文件指针
返回值:成功返回0,失败返回EOF(-1)
避坑指南:
- 🔴 打开文件后必须使用
fclose函数关闭文件!否则会导致文件资源泄漏 - ✅ 关闭文件后,文件指针会变成悬空指针,必须设置为
NULL,避免野指针问题 - 🔴 关闭空指针会导致程序崩溃
代码示例5:使用fclose函数关闭文件
#include <stdio.h>
int main() {
FILE *fp = fopen("test.txt", "r");
if (fp == NULL) {
printf("文件打开失败!\n");
return 1;
}
printf("文件打开成功!\n");
if (fclose(fp) == 0) {
printf("文件关闭成功!\n");
} else {
printf("文件关闭失败!\n");
}
fp = NULL;
return 0;
}
四、模块3:文本文件读写操作——处理字符数据
4.1 学习目标
- 掌握字符读写函数
fgetc和fputc的用法 - 掌握行读写函数
fgets和fputs的用法 - 掌握格式化读写函数
fscanf和fprintf的用法 - 避开文本文件读写操作的4大常见坑
4.2 字符读写函数fgetc和fputc
4.2.1 fgetc函数
功能:从文件中读取一个字符
语法:
int fgetc(FILE *fp);
返回值:成功返回读取到的字符的ASCII值,失败或读到文件末尾返回EOF(-1)
代码示例6:使用fgetc函数读取文件
#include <stdio.h>
int main() {
FILE *fp = fopen("test.txt", "r");
if (fp == NULL) {
printf("文件打开失败!\n");
return 1;
}
int ch;
while ((ch = fgetc(fp)) != EOF) {
putchar(ch);
}
fclose(fp);
fp = NULL;
return 0;
}
4.2.2 fputc函数
功能:向文件中写入一个字符
语法:
int fputc(int ch, FILE *fp);
返回值:成功返回写入的字符的ASCII值,失败返回EOF(-1)
代码示例7:使用fputc函数写入文件
#include <stdio.h>
int main() {
FILE *fp = fopen("output.txt", "w");
if (fp == NULL) {
printf("文件打开失败!\n");
return 1;
}
char str[] = "Hello, World!";
int i = 0;
while (str[i] != '\0') {
fputc(str[i], fp);
i++;
}
fclose(fp);
fp = NULL;
return 0;
}
4.3 行读写函数fgets和fputs
4.3.1 fgets函数
功能:从文件中读取一行字符
语法:
char *fgets(char *str, int size, FILE *fp);
参数:
-
str:存放读取到的字符串的缓冲区 -
size:缓冲区的大小 -
fp:文件指针
返回值:成功返回指向str的指针,失败或读到文件末尾返回NULL
代码示例8:使用fgets函数读取文件
#include <stdio.h>
int main() {
FILE *fp = fopen("test.txt", "r");
if (fp == NULL) {
printf("文件打开失败!\n");
return 1;
}
char buffer[100];
while (fgets(buffer, 100, fp) != NULL) {
printf("%s", buffer);
}
fclose(fp);
fp = NULL;
return 0;
}
4.3.2 fputs函数
功能:向文件中写入一行字符
语法:
int fputs(const char *str, FILE *fp);
参数:
-
str:要写入的字符串 -
fp:文件指针
返回值:成功返回非负整数,失败返回EOF(-1)
代码示例9:使用fputs函数写入文件
#include <stdio.h>
int main() {
FILE *fp = fopen("output.txt", "w");
if (fp == NULL) {
printf("文件打开失败!\n");
return 1;
}
char lines[] = "第一行\n第二行\n第三行\n";
fputs(lines, fp);
fclose(fp);
fp = NULL;
return 0;
}
4.4 格式化读写函数fscanf和fprintf
4.4.1 fscanf函数
功能:从文件中读取格式化的数据
语法:
int fscanf(FILE *fp, const char *format, ...);
参数:
-
fp:文件指针 -
format:格式化字符串(与scanf函数相同) -
...:输入参数列表
返回值:成功读取到的数据项数,失败返回EOF(-1)
代码示例10:使用fscanf函数读取文件
#include <stdio.h>
int main() {
FILE *fp = fopen("data.txt", "r");
if (fp == NULL) {
printf("文件打开失败!\n");
return 1;
}
int num;
while (fscanf(fp, "%d", &num) == 1) {
printf("%d\n", num);
}
fclose(fp);
fp = NULL;
return 0;
}
4.4.2 fprintf函数
功能:向文件中写入格式化的数据
语法:
int fprintf(FILE *fp, const char *format, ...);
参数:
-
fp:文件指针 -
format:格式化字符串(与printf函数相同) -
...:输出参数列表
返回值:成功写入的字符数,失败返回EOF(-1)
代码示例11:使用fprintf函数写入文件
#include <stdio.h>
typedef struct {
char name[50];
int age;
float score;
} Student;
int main() {
FILE *fp = fopen("students.txt", "w");
if (fp == NULL) {
printf("文件打开失败!\n");
return 1;
}
Student students[] = {
{"张三", 18, 95.5},
{"李四", 19, 88.0},
{"王五", 20, 92.5}
};
int length = sizeof(students) / sizeof(students[0]);
for (int i = 0; i < length; i++) {
fprintf(fp, "%s %d %.1f\n", students[i].name, students[i].age, students[i].score);
}
fclose(fp);
fp = NULL;
return 0;
}
五、模块4:二进制文件读写操作——处理字节数据
5.1 学习目标
- 掌握块读写函数
fread和fwrite的用法 - 学会使用
fread和fwrite函数读写结构体数据 - 理解二进制文件与文本文件的区别
- 避开二进制文件读写操作的3大常见坑
5.2 块读写函数fread和fwrite
5.2.1 fread函数
功能:从文件中读取数据块
语法:
size_t fread(void *ptr, size_t size, size_t count, FILE *fp);
参数:
-
ptr:存放读取到的数据的缓冲区 -
size:每个数据项的大小 -
count:要读取的数据项数 -
fp:文件指针
返回值:成功读取到的数据项数
代码示例12:使用fread函数读取二进制文件
#include <stdio.h>
typedef struct {
char name[50];
int age;
float score;
} Student;
int main() {
FILE *fp = fopen("students.bin", "rb");
if (fp == NULL) {
printf("文件打开失败!\n");
return 1;
}
Student student;
while (fread(&student, sizeof(Student), 1, fp) == 1) {
printf("姓名:%s\n", student.name);
printf("年龄:%d\n", student.age);
printf("成绩:%.1f\n", student.score);
printf("\n");
}
fclose(fp);
fp = NULL;
return 0;
}
5.2.2 fwrite函数
功能:向文件中写入数据块
语法:
size_t fwrite(const void *ptr, size_t size, size_t count, FILE *fp);
参数:
-
ptr:要写入的数据的缓冲区 -
size:每个数据项的大小 -
count:要写入的数据项数 -
fp:文件指针
返回值:成功写入的数据项数
代码示例13:使用fwrite函数写入二进制文件
#include <stdio.h>
typedef struct {
char name[50];
int age;
float score;
} Student;
int main() {
FILE *fp = fopen("students.bin", "wb");
if (fp == NULL) {
printf("文件打开失败!\n");
return 1;
}
Student students[] = {
{"张三", 18, 95.5},
{"李四", 19, 88.0},
{"王五", 20, 92.5}
};
int length = sizeof(students) / sizeof(students[0]);
fwrite(students, sizeof(Student), length, fp);
fclose(fp);
fp = NULL;
return 0;
}
六、模块5:文件定位操作——控制读写位置
6.1 学习目标
- 掌握文件定位函数
ftell、fseek、rewind的用法 - 理解文件指针的移动原理
- 学会使用
fseek函数实现随机读写 - 避开文件定位操作的3大常见坑
6.2 文件定位函数
6.2.1 ftell函数
功能:获取文件指针当前的位置
语法:
long ftell(FILE *fp);
参数:
-
fp:文件指针
返回值:成功返回文件指针的位置,失败返回-1
代码示例14:使用ftell函数获取文件指针位置
#include <stdio.h>
int main() {
FILE *fp = fopen("test.txt", "r");
if (fp == NULL) {
printf("文件打开失败!\n");
return 1;
}
fseek(fp, 5, SEEK_SET); // 移动到文件开头后5字节的位置
long pos = ftell(fp);
printf("文件指针当前位置:%ld\n", pos); // 输出:5
fclose(fp);
fp = NULL;
return 0;
}
6.2.2 fseek函数
功能:移动文件指针到指定位置
语法:
int fseek(FILE *fp, long offset, int origin);
参数:
-
fp:文件指针 -
offset:偏移量(字节数) -
origin:起始位置(SEEK_SET:文件开头,SEEK_CUR:当前位置,SEEK_END:文件末尾)
返回值:成功返回0,失败返回-1
代码示例15:使用fseek函数移动文件指针
#include <stdio.h>
int main() {
FILE *fp = fopen("test.txt", "r");
if (fp == NULL) {
printf("文件打开失败!\n");
return 1;
}
// 移动到文件开头后5字节的位置
fseek(fp, 5, SEEK_SET);
char buffer[100];
fgets(buffer, 100, fp);
printf("从位置5读取:%s", buffer);
// 移动到当前位置后3字节的位置
fseek(fp, 3, SEEK_CUR);
fgets(buffer, 100, fp);
printf("从位置%d读取:%s", ftell(fp), buffer);
// 移动到文件末尾前5字节的位置
fseek(fp, -5, SEEK_END);
fgets(buffer, 100, fp);
printf("从位置%d读取:%s", ftell(fp), buffer);
fclose(fp);
fp = NULL;
return 0;
}
6.2.3 rewind函数
功能:将文件指针移动到文件开头
语法:
void rewind(FILE *fp);
参数:
-
fp:文件指针
代码示例16:使用rewind函数移动文件指针到开头
#include <stdio.h>
int main() {
FILE *fp = fopen("test.txt", "r");
if (fp == NULL) {
printf("文件打开失败!\n");
return 1;
}
fseek(fp, 5, SEEK_SET);
long pos = ftell(fp);
printf("文件指针位置:%ld\n", pos); // 输出:5
rewind(fp);
pos = ftell(fp);
printf("文件指针位置:%ld\n", pos); // 输出:0
fclose(fp);
fp = NULL;
return 0;
}
七、本章总结与课后练习
7.1 总结
✅ 文件操作的基本概念:将数据从内存保存到磁盘(持久化)或从磁盘读取数据到内存
✅ 文件打开与关闭:使用fopen函数打开文件,使用fclose函数关闭文件
✅ 文本文件读写操作:使用fgetc/fputc、fgets/fputs、fscanf/fprintf函数读写文本文件
✅ 二进制文件读写操作:使用fread/fwrite函数读写二进制文件,效率更高
✅ 文件定位操作:使用ftell、fseek、rewind函数控制读写位置
7.2 课后练习
- 编写程序:统计文本文件的行数、字数、字符数
- 编写程序:实现一个简单的文本编辑器(支持打开、保存、编辑功能)
- 编写程序:将文本文件的内容复制到另一个文件
- 编写程序:使用二进制文件读写操作实现一个简单的学生管理系统(添加、删除、查找、排序)