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 偏移量、读取更大范围的数据,加深对不同模式与函数的理解。遇到报错时,优先检查文件是否成功打开、路径是否正确,以及模式是否与操作匹配。

相关推荐
小张成长计划..44 分钟前
【Linux】26:线程互斥和同步
java·开发语言
阿里嘎多学长9 小时前
2026-10-20 GitHub 热点项目精选
开发语言·程序员·github·代码托管
布吉岛的石头9 小时前
Java 程序员第 50 阶段2:预训练的目标函数:自回归语言建模本质
java·开发语言·人工智能·深度学习·回归·transformer
小羊没烦恼!10 小时前
铁道部新客票系统设计(二)
开发语言·前端·数据库·python·oracle·c#·php
quantdash_cc11 小时前
从实时行情到价格观察表:用 Python 和 Pandas 构建策略输入数据
开发语言·python·数据分析·pandas·量化交易·股票数据·quantdash
昂好的day11 小时前
VisionMaster 通信模块实战(上):Modbus、TCP 收发、协议组装解析
开发语言·网络·网络协议·tcp/ip·c#
努力实干12 小时前
面向对象设计原则——C++
开发语言·c++·数码相机
NetX行者14 小时前
四语言 Hello World 背后:编译模型与运行时对比
开发语言
Pin_Qing15 小时前
账单没超,请求先被拒了
大数据·开发语言·人工智能