C语言中,fprintffscanf函数实现文件的格式化读写,通过类似printf/scanf的格式控制字符串将数据(如整数、字符串、浮点数)以可读文本形式写入或读取文件,适用于配置文件、日志记录等场景;相比二进制读写,其优势在于生成的文件内容人类可直接阅读和编辑,但读写效率较低。

C语言文件格式化读写是处理复杂数据(如数字、结构体等)的高效方式,使用fscanf(格式化读取)和fprintf(格式化写入)函数,它们与scanf/printf类似,只是多了一个文件指针参数。

一、核心函数介绍

1. fprintf函数:格式化写入文件

  • 函数原型int fprintf(FILE *stream, const char *format, ...);
  • 参数说明
    • stream:文件指针(写入文件传文件指针,写入屏幕传stdout
    • format:格式控制字符串(与printf完全一致)
    • ...:要写入的变量/数据
  • 返回值:成功写入的字符数,失败返回负数

2. fscanf函数:格式化读取文件

  • 函数原型int fscanf(FILE *stream, const char *format, ...);
  • 参数说明
    • stream:文件指针
    • format:格式控制字符串
    • ...:接收数据的变量地址
  • 返回值:成功读取的参数个数,失败返回EOF

二、基本用法示例

1. 写入文件示例

#include <stdio.h>

struct Student {
    char name[20];
    int age;
    double score;
};

int main() {
    // 定义多个学生数据(示例:3个学生)
    struct Student students[] = {
        {"张三", 18, 95.5},
        {"李四", 20, 88.0},
        {"王五", 19, 92.5}
    };
    int num_students = sizeof(students) / sizeof(students[0]); // 计算学生数量

    // 打开文件(以写入模式,覆盖原有内容)
    FILE *pf = fopen("student.txt", "w");
    if (pf == NULL) {
        perror("fopen");
        return 1;
    }

    // 循环写入多行数据
    for (int i = 0; i < num_students; i++) {
        fprintf(pf, "姓名:%s 年龄:%d 分数:%.1lf\n",
                students[i].name, students[i].age, students[i].score);
    }

    // 关闭文件
    fclose(pf);
    pf = NULL;

    // 验证写入结果(可选:打印到控制台)
    printf("已成功写入 %d 条学生记录到 student.txt\n", num_students);

    return 0;
}

2. 读取文件示例

#include <stdio.h>

struct Student {
    char name[20];
    int age;
    double score;
};

int main() {
    // 定义用于存储读取数据的结构体
    struct Student s;

    // 打开文件(以读取模式)
    FILE *pf = fopen("student.txt", "r");
    if (pf == NULL) {
        perror("fopen");
        return 1;
    }

    // 读取文件内容
    // printf("读取到的学生记录:\n");
    // while (fscanf(pf, "姓名:%19s 年龄:%d 分数:%lf", s.name, &s.age, &s.score) == 3) {
    //     printf("姓名:%s 年龄:%d 分数:%.1lf\n",s.name, s.age, s.score);
    // }
    char line[100];
    printf("读取到的学生记录:\n");
    while (fgets(line, sizeof(line), pf) != NULL) {
        struct Student s;
        // 用 sscanf 解析整行(自动跳过空白)
        if (sscanf(line, "姓名:%19s 年龄:%d 分数:%lf", s.name, &s.age, &s.score) == 3) {
            printf("姓名:%s 年龄:%d 分数:%.1lf\n", s.name, s.age, s.score);
        }
    }

    // 错误处理
    if (ferror(pf)) {
        fprintf(stderr, "读取文件时发生错误: %s\n", strerror(errno));
        fclose(pf);
        return 1;
    }

    // 关闭文件
    fclose(pf);
    pf = NULL;

    return 0;
}

三、格式控制字符串详解

格式控制字符串使用%开头的格式说明符,与printf相同:

  • %d:整数
  • %f:浮点数
  • %s:字符串
  • %c:字符
  • %.2f:保留两位小数的浮点数
  • %49[^,]:读取最多49个字符,直到遇到逗号

四、重要注意事项

  1. 文件打开模式

    • "r":只读模式(文件必须存在)
    • "w":写入模式(如果文件存在则清空,不存在则创建)
    • "a":追加模式(在文件末尾追加)
    • "rb"/"wb"/"ab":二进制模式
  2. 错误处理

    • 必须检查fopen返回值
    • 使用ferror检查文件操作错误
    • 使用strerror(errno)获取错误信息
  3. 文件指针操作

    • fseek:移动文件指针
    • ftell:获取文件指针当前位置
    • rewind:将文件指针重置到文件开头

五、与普通输入输出的区别

  • fscanf vs scanf

    • fscanf:从文件读取数据
    • scanf:从标准输入(键盘)读取数据
    • 若将fscanf的文件指针设为stdin,则等同于scanf
  • fprintf vs printf

    • fprintf:向文件写入数据
    • printf:向标准输出(屏幕)写入数据
    • 若将fprintf的文件指针设为stdout,则等同于printf

六、实际应用场景

  1. 配置文件读写:存储应用程序配置参数
  2. 日志文件:记录程序运行信息
  3. 数据备份:将程序数据保存到文件
  4. 数据交换:不同系统间交换结构化数据

格式化读写的优势在于生成的文件是可读的,不但程序能识别,用户也可以看懂并手动修改,这与二进制读写(生成的文件不可读)形成鲜明对比。

七、与二进制读写的对比

特性格式化读写二进制读写
文件内容可读文本二进制数据
适用场景配置文件、日志文件高效存储大型数据
读写速度较慢较快
代码复杂度简单稍复杂
跨平台性依赖平台

例如,使用格式化读写生成的文件内容:

姓名:张三 年龄:18 分数:95.5

而使用二进制读写生成的文件内容是不可读的二进制数据。

以上是C语言文件格式化读写的核心内容,通过合理使用fscanffprintf,可以高效处理各种文件数据交互需求。