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)

避坑指南

  1. 🔴 打开文件后必须使用fclose函数关闭文件!否则会导致文件资源泄漏
  2. ✅ 关闭文件后,文件指针会变成悬空指针,必须设置为NULL,避免野指针问题
  3. 🔴 关闭空指针会导致程序崩溃

代码示例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 学习目标

  • 掌握字符读写函数fgetcfputc的用法
  • 掌握行读写函数fgetsfputs的用法
  • 掌握格式化读写函数fscanffprintf的用法
  • 避开文本文件读写操作的4大常见坑

4.2 字符读写函数fgetcfputc

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 行读写函数fgetsfputs

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 格式化读写函数fscanffprintf

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 学习目标

  • 掌握块读写函数freadfwrite的用法
  • 学会使用freadfwrite函数读写结构体数据
  • 理解二进制文件与文本文件的区别
  • 避开二进制文件读写操作的3大常见坑

5.2 块读写函数freadfwrite

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 学习目标

  • 掌握文件定位函数ftellfseekrewind的用法
  • 理解文件指针的移动原理
  • 学会使用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/fputcfgets/fputsfscanf/fprintf函数读写文本文件
二进制文件读写操作:使用fread/fwrite函数读写二进制文件,效率更高
文件定位操作:使用ftellfseekrewind函数控制读写位置

7.2 课后练习

  1. 编写程序:统计文本文件的行数、字数、字符数
  2. 编写程序:实现一个简单的文本编辑器(支持打开、保存、编辑功能)
  3. 编写程序:将文本文件的内容复制到另一个文件
  4. 编写程序:使用二进制文件读写操作实现一个简单的学生管理系统(添加、删除、查找、排序)
© 版权声明

相关文章