【C语言程序设计】第34篇:文件的概念与文件指针

1 引言

到目前为止,我们所有的程序数据都存储在内存中,程序结束后就消失了。但在实际应用中,我们需要将数据长期保存,比如:

  • 保存用户配置,下次启动时读取

  • 记录程序运行日志

  • 处理外部数据文件

  • 生成报告文档

这就需要用到文件操作。C语言通过文件指针和一系列库函数提供了对文件的访问能力。

c

复制代码
#include <stdio.h>

int main(void)
{
    FILE *fp;  /* 文件指针 */
    
    /* 打开文件 */
    fp = fopen("test.txt", "w");
    if (fp == NULL) {
        printf("文件打开失败\n");
        return 1;
    }
    
    /* 写入数据 */
    fprintf(fp, "Hello, File!\n");
    
    /* 关闭文件 */
    fclose(fp);
    
    return 0;
}

本章我们将深入理解文件操作背后的概念和机制。


2 文件的基本概念

2.1 什么是文件

文件(File)是存储在外部介质(如磁盘)上的数据的集合,是程序进行输入输出的基本单位。操作系统以文件为单位管理数据。

从程序的角度看,文件就是字节序列------一系列可以读取或写入的字节。

2.2 文件的分类

根据数据的组织形式,文件分为两大类:

文件类型 特点 示例
文本文件 以字符为单位,每字节存储一个字符的ASCII码 .txt, .c, .html
二进制文件 以字节为单位,按内存中的存储形式原样存放 .exe, .jpg, .mp3

2.3 文本文件

文本文件存储的是字符序列,每个字节对应一个字符的ASCII码。

c

复制代码
/* 在文本文件中存储整数 12345 */
fprintf(fp, "%d", 12345);
/* 实际写入:'1' '2' '3' '4' '5' 共5字节 */

特点

  • 内容可以直接用文本编辑器查看

  • 每行通常以换行符结束(不同系统换行符不同)

  • 读写时需要格式转换(如数字与字符串的转换)

  • 占用空间通常比二进制大

2.4 二进制文件

二进制文件按照数据在内存中的二进制形式原样存储。

c

复制代码
/* 在二进制文件中存储整数 12345 */
int n = 12345;
fwrite(&n, sizeof(int), 1, fp);
/* 实际写入:int 的二进制表示,通常4字节 */

特点

  • 不能用文本编辑器直接查看(可能显示乱码)

  • 读写速度快,无需格式转换

  • 存储紧凑,占用空间小

  • 跨平台可能存在字节序问题

2.5 文本文件 vs 二进制文件

比较维度 文本文件 二进制文件
存储形式 字符的ASCII码 数据的二进制形式
可读性 可直接阅读 不可直接阅读
空间效率 较低(如数字12345占5字节) 较高(int 12345占4字节)
读写效率 需格式转换,较慢 直接读写,较快
换行符处理 系统自动转换(\n ↔ \r\n) 不处理,原样写入
适用场景 配置文件、源代码、文档 可执行文件、图像、音频

3 流的概念

3.1 什么是流

(Stream)是C语言中对输入输出设备的抽象。它提供了一种统一的方式,无论数据来自键盘、屏幕、文件还是网络,程序都可以用相同的方式读写。

text

复制代码
程序 ←→ 流 ←→ 设备(文件、键盘、屏幕等)

3.2 流的分类

C语言中的流分为两类:

流类型 说明 示例
文本流 以行为单位,处理换行符转换 文本文件
二进制流 字节原样传输,不转换 二进制文件

3.3 标准流

当C程序启动时,系统会自动打开三个标准流:

流名称 文件指针 默认设备 用途
标准输入 stdin 键盘 读取输入
标准输出 stdout 屏幕 输出正常信息
标准错误 stderr 屏幕 输出错误信息

c

复制代码
#include <stdio.h>

int main(void)
{
    int n;
    
    fprintf(stdout, "请输入一个整数:");  /* 正常输出 */
    fscanf(stdin, "%d", &n);              /* 标准输入 */
    
    if (n < 0) {
        fprintf(stderr, "错误:输入不能为负数\n");  /* 错误输出 */
        return 1;
    }
    
    return 0;
}

4 FILE 结构体与文件指针

4.1 FILE 结构体

FILE 是C语言标准库定义的一个结构体类型,用于保存文件的相关信息。它通常包含:

  • 文件位置指示器(当前读写位置)

  • 缓冲区指针和缓冲区大小

  • 文件状态标志(是否到达末尾、是否有错误等)

  • 文件描述符(操作系统层面的文件标识)

c

复制代码
/* 在 stdio.h 中的典型定义(简化示意) */
typedef struct {
    int level;       /* 缓冲区填充程度 */
    unsigned flags;  /* 文件状态标志 */
    char fd;         /* 文件描述符 */
    unsigned char hold; /* 无缓冲区时读取的字符 */
    int bsize;       /* 缓冲区大小 */
    unsigned char *buffer; /* 数据缓冲区 */
    unsigned char *curp;   /* 当前活动指针 */
    unsigned istemp; /* 临时文件标志 */
    short token;     /* 用于有效性检查 */
} FILE;

注意 :在实际编程中,我们不需要 关心 FILE 结构体的内部细节,只需要通过 FILE* 指针使用标准库函数即可。

4.2 文件指针

文件指针 (File Pointer)是指向 FILE 结构体的指针,用于标识一个流,是所有文件操作函数的核心参数。

c

复制代码
FILE *fp;  /* 定义文件指针 */

文件指针的作用:

  • 标识打开的文件

  • 保存文件的状态信息

  • 传递给文件操作函数(如 fprintffscanffreadfwrite

4.3 文件指针与普通指针的区别

方面 文件指针 普通指针
指向内容 FILE 结构体 任意类型的数据
操作方式 通过库函数(fread/fwrite等) 通过解引用(*)
使用者 库函数内部维护 程序员直接操作

5 文件操作的基本流程

5.1 三步曲

文件操作通常遵循三个步骤:

  1. 打开文件fopen() 返回文件指针

  2. 读写文件fprintf()fscanf()fread()fwrite()

  3. 关闭文件fclose() 释放资源

c

复制代码
#include <stdio.h>

int main(void)
{
    FILE *fp;
    
    /* 1. 打开文件 */
    fp = fopen("example.txt", "r");
    if (fp == NULL) {
        perror("文件打开失败");
        return 1;
    }
    
    /* 2. 读写文件(这里只是示例) */
    char buffer[100];
    while (fgets(buffer, sizeof(buffer), fp) != NULL) {
        printf("%s", buffer);
    }
    
    /* 3. 关闭文件 */
    fclose(fp);
    
    return 0;
}

5.2 为什么需要关闭文件

关闭文件(fclose)非常重要,原因如下:

  1. 刷新缓冲区:确保所有数据真正写入磁盘

  2. 释放资源 :释放 FILE 结构体和系统文件句柄

  3. 防止数据丢失:程序异常结束时,缓冲区数据可能丢失

c

复制代码
/* 错误:忘记关闭文件 */
FILE *fp = fopen("data.txt", "w");
fprintf(fp, "重要数据");
/* 程序结束,但数据可能还在缓冲区,未写入磁盘! */

6 文件打开模式

fopen 的第二个参数指定打开模式,常用的有:

6.1 文本文件模式

模式 含义 文件存在时 文件不存在时
"r" 只读 正常打开 返回 NULL
"w" 只写 内容被清空 创建新文件
"a" 追加 从末尾写入 创建新文件
"r+" 读写 正常打开 返回 NULL
"w+" 读写 内容被清空 创建新文件
"a+" 读和追加 从末尾写入 创建新文件

6.2 二进制文件模式

在文本模式后加 b 表示二进制模式:

模式 含义
"rb" 二进制只读
"wb" 二进制只写
"ab" 二进制追加
"rb+" 二进制读写
"wb+" 二进制读写(清空)
"ab+" 二进制读和追加

6.3 模式选择示例

c

复制代码
FILE *fp;

fp = fopen("data.txt", "r");   /* 读取文本文件 */
fp = fopen("data.txt", "w");   /* 写入文本文件(覆盖) */
fp = fopen("log.txt", "a");    /* 追加到文本文件末尾 */

fp = fopen("image.jpg", "rb"); /* 读取二进制文件 */
fp = fopen("data.bin", "wb");  /* 写入二进制文件 */

7 常见错误与注意事项

7.1 忘记检查文件是否成功打开

c

复制代码
FILE *fp = fopen("nonexistent.txt", "r");
fprintf(fp, "test");  /* 如果 fp 为 NULL,程序崩溃! */

/* 正确做法 */
FILE *fp = fopen("nonexistent.txt", "r");
if (fp == NULL) {
    perror("打开文件失败");
    return 1;
}

7.2 忘记关闭文件

c

复制代码
void process_file(void)
{
    FILE *fp = fopen("data.txt", "r");
    /* 处理文件... */
    /* 忘记 fclose(fp) */
}  /* 文件句柄泄漏!程序运行期间无法再次打开该文件 */

7.3 混淆文本模式和二进制模式

在 Windows 系统上,文本模式下换行符 \n 会被自动转换为 \r\n,二进制模式下则不会。

c

复制代码
/* 在 Windows 上 */
FILE *fp = fopen("test.txt", "w");
fprintf(fp, "Hello\nWorld");  /* 实际写入:Hello\r\nWorld */

fp = fopen("test.bin", "wb");
fprintf(fp, "Hello\nWorld");  /* 实际写入:Hello\nWorld */

7.4 对同一文件进行多次打开

同一个文件可以被多次打开,每次得到不同的文件指针,各自维护独立的位置指示器。

c

复制代码
FILE *fp1 = fopen("data.txt", "r");
FILE *fp2 = fopen("data.txt", "r");

fgetc(fp1);  /* 读取第一个字符,fp1 位置前移 */
fgetc(fp2);  /* 也读取第一个字符,fp2 位置独立 */

7.5 使用已关闭的文件指针

c

复制代码
FILE *fp = fopen("data.txt", "r");
fclose(fp);
fprintf(fp, "test");  /* 错误!fp 已无效 */

7.6 忽略错误处理

文件操作可能因各种原因失败(磁盘满、权限不足等),应该检查返回值:

c

复制代码
if (fprintf(fp, "data") < 0) {
    perror("写入失败");
}

8 本章小结

本章系统介绍了文件的基本概念和文件指针:

1. 文件的类型

  • 文本文件:存储字符的ASCII码,可直接阅读

  • 二进制文件:存储数据的二进制形式,紧凑高效

2. 流的概念

  • 程序与设备之间的抽象,提供统一接口

  • 标准流:stdinstdoutstderr

  • 文本流和二进制流的区别

3. FILE 结构体与文件指针

  • FILE 结构体保存文件的状态信息

  • 文件指针是 FILE* 类型,标识一个流

  • 程序员只需使用指针,不关心内部细节

4. 文件操作三步曲

  1. fopen() 打开文件(检查返回值)

  2. 读写操作

  3. fclose() 关闭文件(必须!)

5. 打开模式

  • "r":只读

  • "w":写入(清空)

  • "a":追加

  • "r+""w+""a+":读写模式

  • b 表示二进制模式

6. 常见错误

  • 忘记检查 fopen 返回值

  • 忘记关闭文件

  • 混淆文本和二进制模式

  • 使用已关闭的文件指针


参考资料

1\] C语言标准(ISO/IEC 9899:2018)- 第7.21节:文件访问 \[2\] Kernighan, B. W., \& Ritchie, D. M. (1988). The C Programming Language (2nd ed.). Prentice Hall. \[3\] Prinz, P., \& Crawford, T. (2005). C in a Nutshell. O'Reilly Media. \[4\] 腾讯云开发者社区. C语言文件操作详解 \[5\] CSDN博客. 文本文件与二进制文件的区别 \[6\] 阿里云开发者社区. FILE结构体深度剖析

相关推荐
大熊背2 小时前
双目拼接摄像机中简单的亮度差校正原理
人工智能·算法·双目拼接·亮度差消除
CoovallyAIHub2 小时前
AAAI 2026 | 上海AI Lab发布RacketVision,首次为球拍运动标注球拍姿态
深度学习·算法·计算机视觉
大熊背2 小时前
双目拼接摄像机中简单的色差校正原理
人工智能·算法·isppipeline·双目拼接
CoovallyAIHub2 小时前
中文语音识别该用谁?6 个开源模型 + 2 个配套工具,一文理清
深度学习·算法·计算机视觉
弦有三种苦难2 小时前
CCF-202412-T3缓存模拟90分
java·开发语言·spring
会编程的土豆2 小时前
【数据结构与算法】 二叉树做题
开发语言·数据结构·c++·算法
木禾ali0th2 小时前
告别大模型“裸奔”:开源项目 ClawVault 架构与核心能力解析
算法·安全
青槿吖2 小时前
SpringMVC通关秘籍(下):日期转换器、拦截器与文件上传的奇幻冒险
java·开发语言·数据库·sql·mybatis·状态模式
Storynone2 小时前
【Day28】LeetCode:509. 斐波那契数,70. 爬楼梯,746. 使用最小花费爬楼梯
python·算法·leetcode