C 语言文件操作详解:从写入、追加到读取与定位的完整 实战

一、引言

文件操作是C语言学习中最实用、也最容易出错的环节之一。无论是处理文本数据、保存程序结果,还是读写配置文件,几乎都离不开对文件的读写控制。很多初学者在学到这里时,常常分不清各种打开模式的区别,也搞不懂二进制读写与文本读写的差异。

本文通过一个完整、可运行的C语言示例程序,系统地讲解文件的写入、追加、逐行读取、二进制读写以及文件定位五类操作,并对每段代码做逐行解析。读完本文,你将能够独立使用 fopen、fclose、fprintf、fputs、fgets、fwrite、fread、fseek 等常用函数完成日常的文件处理任务。

二、环境准备

本文示例基于标准C语言编写,使用任意主流的C语言编译器均可运行,推荐使用 Dev-C++、Visual Studio 或 CodeBlocks。程序运行时,会在当前工作目录下自动生成 test.txt(文本文件)与 binary.bin(二进制文件)。

示例中用到的头文件及作用如下:

三、文件操作核心概念

3.1 FILE 指针

在C语言中,所有的文件操作都通过一个名为 FILE 的结构体指针完成。当我们调用 fopen 打开一个文件时,系统会返回一个指向该文件信息的 FILE 指针,后续的读写函数都依赖这个指针定位到目标文件。若打开失败,fopen 会返回 NULL,因此在每次打开文件后都必须判断返回值。

3.2 fopen 的打开模式

fopen 函数的原型为 FILE *fopen(const char *filename, const char *mode),其中第二个参数 mode 决定了文件的打开方式。常见的模式如下:

3.3 关闭文件

文件使用完毕后,必须调用 fclose(FILE *fp) 关闭文件。及时关闭文件可以释放系统资源,并确保缓冲区中的内容真正写入磁盘,避免数据丢失。

四、完整示例代码

下面给出本文的完整示例程序,代码分为写入、追加、读取、二进制读写、文件定位五个部分:

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_LENGTH 100

int main() {
    FILE *fp;
    char filename[] = "test.txt";
    char buffer[MAX_LENGTH];

    // 1. 写入文件
    fp = fopen(filename, "w");  // 只写,覆盖模式
    if (fp == NULL) { perror("无法打开文件"); return 1; }
    fprintf(fp, "Hello, World!\n");   // 格式化写入
    fputs("This is a test file.\n", fp);  // 写入字符串
    fclose(fp);

    // 2. 追加内容
    fp = fopen(filename, "a");  // 追加模式
    if (fp == NULL) { perror("无法打开文件"); return 1; }
    fputs("Appended line.\n", fp);
    fclose(fp);

    // 3. 逐行读取
    fp = fopen(filename, "r");
    if (fp == NULL) { perror("无法打开文件"); return 1; }
    printf("文件内容:\n");
    while (fgets(buffer, MAX_LENGTH, fp) != NULL) {
        printf("%s", buffer);  // fgets 包含换行符
    }
    fclose(fp);

    // 4. 二进制文件读写
    int numbers[] = {1, 2, 3, 4, 5};
    int read_numbers[5];
    fp = fopen("binary.bin", "wb");  // 二进制写入
    if (fp == NULL) { perror("无法打开二进制文件"); return 1; }
    fwrite(numbers, sizeof(int), 5, fp);  // 写入5个int
    fclose(fp);

    fp = fopen("binary.bin", "rb");  // 二进制读取
    if (fp == NULL) { perror("无法打开二进制文件"); return 1; }
    fread(read_numbers, sizeof(int), 5, fp);  // 读取5个int
    fclose(fp);
    printf("\n二进制文件内容:\n");
    for (int i = 0; i < 5; i++) {
        printf("numbers[%d] = %d\n", i, read_numbers[i]);
    }

    // 5. 文件定位:读取文件末尾前10个字节
    fp = fopen(filename, "r");
    if (fp == NULL) { perror("无法打开文件"); return 1; }
    fseek(fp, -10, SEEK_END);  // 移动到末尾前10字节
    char last_chars[11] = {0};
    fread(last_chars, 1, 10, fp);
    printf("\n文件最后10个字符: %s\n", last_chars);
    fclose(fp);
    return 0;
}

五、代码逐模块解析

5.1 文本文件写入("w" 模式)

第一段代码以 "w" 模式打开 test.txt,使用 fprintf 进行格式化写入,再使用 fputs 写入一个字符串。要点如下:

cpp 复制代码
fp = fopen(filename, "w"); if (fp == NULL) { perror("无法打开文件"); return 1; } fprintf(fp, "Hello, World!\n"); fputs("This is a test file.\n", fp); fclose(fp);

"w" 模式会清空文件原有内容再从头写入,若文件不存在则自动创建。fprintf 与 printf 用法几乎一致,只是多了一个 FILE 指针作为第一个参数;fputs 则用于向文件写入一个字符串(不自动追加换行符,因此示例中手动加上 \n)。

5.2 追加内容("a" 模式)

第二段代码以 "a" 模式重新打开同一个文件并追加一行内容。与 "w" 不同,"a" 模式不会覆盖原有内容,而是将新内容追加到文件末尾:

cpp 复制代码
fp = fopen(filename, "a"); if (fp == NULL) { perror("无法打开文件"); return 1; } fputs("Appended line.\n", fp); fclose(fp);

5.3 逐行读取文件("r" 模式)

第三段代码以 "r" 模式打开文件,使用 fgets 循环逐行读取。fgets 每次最多读取 MAX_LENGTH-1 个字符,直到遇到换行符或文件末尾,读取的内容(包含换行符)存放在 buffer 中:

cpp 复制代码
fp = fopen(filename, "r"); if (fp == NULL) { perror("无法打开文件"); return 1; } printf("文件内容:\n"); while (fgets(buffer, MAX_LENGTH, fp) != NULL) { printf("%s", buffer); } fclose(fp);

这里 while 循环的判断条件是 fgets 的返回值是否为 NULL:读到文件末尾时 fgets 会返回 NULL,循环随之结束,从而实现了"逐行读取直到读完"的效果。

5.4 二进制文件读写("wb"/"rb" 模式)

第四段代码演示二进制文件的读写。fwrite 将内存中的 5 个 int 直接以二进制形式写入 binary.bin,fread 再从文件中读回这 5 个整数:

cpp 复制代码
fwrite(numbers, sizeof(int), 5, fp); fread(read_numbers, sizeof(int), 5, fp);

fwrite 与 fread 的函数原型为 size_t fwrite(const void *ptr, size_t size, size_t count, FILE *fp),三个核心参数分别表示:数据起始地址、单个元素的大小(字节数)、元素个数。二进制方式保存的数据与内存中的表示一致,读写效率高,但不适合直接以文本方式查看。

5.5 文件定位(fseek)

最后一段代码使用 fseek 将文件位置指针定位到"文件末尾前 10 个字节",再用 fread 读取最后 10 个字符:

cpp 复制代码
fseek(fp, -10, SEEK_END); fread(last_chars, 1, 10, fp);

fseek 的原型为 int fseek(FILE *fp, long offset, int origin),offset 为相对偏移量(负数表示向前),origin 可取 SEEK_SET(文件开头)、SEEK_CUR(当前位置)、SEEK_END(文件末尾)。由于 last_chars 已初始化为 0,最后打印时能正常以字符串形式输出末尾内容。

六、运行结果

程序运行后,控制台将依次输出读取到的文件内容、二进制读回的整数序列,以及文件末尾的 10 个字符,效果大致如下:

cpp 复制代码
文件内容: 
Hello, World! 
This is a test file. 
Appended line. 
二进制文件内容: 
numbers[0] = 1 
numbers[1] = 2 
numbers[2] = 3 
numbers[3] = 4 
umbers[4] = 5 
文件最后10个字符: e. Appended

可以看到,test.txt 中同时包含第一次写入的两行与追加的一行;binary.bin 中读回的 5 个整数与写入时完全一致;文件定位成功读取到末尾的 10 个字符。

七、易错点与注意事项

八、总结

本文通过一个完整的示例程序,系统讲解了C语言文件的五大操作:使用 "w" 模式写入、用 "a" 模式追加、以 "r" 模式逐行读取、通过 fwrite/fread 进行二进制读写,以及借助 fseek 实现文件定位。掌握这些操作后,你就可以灵活地处理各类文本与二进制文件。

建议读者动手将示例代码编译运行一遍,并尝试修改文件路径、改变 fseek 偏移量、读取更大范围的数据,加深对不同模式与函数的理解。遇到报错时,优先检查文件是否成功打开、路径是否正确,以及模式是否与操作匹配。

相关推荐
ShineWinsu1 小时前
对于Qt:QListWidget常见属性以及QListWidgetItem类的解析
开发语言·c++·qt·客户端·列表·qlistwidget·qlistwidgetitem
吴名氏.1 小时前
Java转AI Agent教程(第十二周)检索与切分
java·开发语言·人工智能·ai·ai agent
瑞雪兆丰年兮2 小时前
JavaWeb-day10-SpringBootWeb案例
java·开发语言·javaweb·web
Elastic 中国社区官方博客2 小时前
更少的位数,相同的召回率:DiskBBQ 更保守的自动校准扫描
大数据·运维·开发语言·人工智能·elasticsearch·搜索引擎
三雷科技2 小时前
第6章第4节:自定义分词器
开发语言·c#
Adellle3 小时前
设计模式指南 (Java 版) ㊁
java·开发语言·设计模式
Delightrs3 小时前
Rust LeetCode238 算法题解
开发语言·算法·rust
云上工程笔记4 小时前
SRT字幕一键转手绘动画:srt-whiteboard-animation的复刻实录
大数据·开发语言·javascript·人工智能·机器学习
zhanghaha13144 小时前
C# 系列教程_12 字符串 string 保姆级系统教程(面向新手,大量示例 + 原理讲解)
开发语言·数据库·c#
eggwyw4 小时前
PHP8.5怎么用ffmpeg提取视频里的字幕轨道
开发语言·php