Skip to content

file io.c

文件信息

  • 📄 原文件:01_file_io.c
  • 🔤 语言:c

完整代码

c
// ============================================================
//                      文件 I/O
// ============================================================
// C 通过 FILE* 句柄和 stdio.h 函数操作文件
// 文件模式:"r"只读、"w"写入(覆盖)、"a"追加
//          "rb"/"wb"二进制模式,"r+"读写
// 【原则】打开文件后必须检查是否成功,用完必须关闭

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

// 临时文件路径
#define TEMP_FILE  "temp_test.txt"
#define TEMP_BIN   "temp_test.bin"

// 结构体(用于二进制文件读写)
typedef struct {
    int    id;
    char   name[32];
    double score;
} Student;

// ============================================================
//                      文本文件操作
// ============================================================

void demo_text_write(void) {
    printf("=== 写入文本文件 ===\n");

    // fopen 打开文件,返回 FILE* 指针
    FILE *fp = fopen(TEMP_FILE, "w");
    if (fp == NULL) {
        fprintf(stderr, "打开文件失败: %s\n", strerror(errno));
        return;
    }

    // fprintf:格式化写入(类似 printf)
    fprintf(fp, "# 学生成绩单\n");
    fprintf(fp, "ID,姓名,分数\n");

    Student students[] = {
        {1, "张三", 95.5},
        {2, "李四", 87.0},
        {3, "王五", 92.5},
    };
    int n = sizeof(students) / sizeof(students[0]);

    for (int i = 0; i < n; i++) {
        fprintf(fp, "%d,%s,%.1f\n",
                students[i].id, students[i].name, students[i].score);
    }

    // fputs:写入字符串
    fputs("# End of File\n", fp);

    // fclose:关闭文件(刷新缓冲区)
    fclose(fp);
    printf("文件已写入: %s\n", TEMP_FILE);
}

void demo_text_read(void) {
    printf("\n=== 读取文本文件 ===\n");

    FILE *fp = fopen(TEMP_FILE, "r");
    if (fp == NULL) {
        fprintf(stderr, "打开文件失败: %s\n", strerror(errno));
        return;
    }

    // 方法1:逐行读取(fgets)
    printf("--- fgets 逐行读取 ---\n");
    char line[256];
    while (fgets(line, sizeof(line), fp) != NULL) {
        // fgets 保留换行符,去掉它
        line[strcspn(line, "\n")] = '\0';
        printf("  |%s|\n", line);
    }
    fclose(fp);

    // 方法2:fscanf 格式化读取
    printf("\n--- fscanf 格式化读取 ---\n");
    fp = fopen(TEMP_FILE, "r");

    char buf[256];
    // 跳过前两行(注释行和表头)
    fgets(buf, sizeof(buf), fp);
    fgets(buf, sizeof(buf), fp);

    int id;
    char name[32];
    double score;
    while (fscanf(fp, "%d,%31[^,],%lf\n", &id, name, &score) == 3) {
        printf("  ID=%d, 姓名=%s, 分数=%.1f\n", id, name, score);
    }
    fclose(fp);
}

// ============================================================
//                      二进制文件操作
// ============================================================

void demo_binary_write(void) {
    printf("\n=== 写入二进制文件 ===\n");

    FILE *fp = fopen(TEMP_BIN, "wb");
    if (fp == NULL) {
        fprintf(stderr, "打开二进制文件失败\n");
        return;
    }

    Student students[] = {
        {1, "张三", 95.5},
        {2, "李四", 87.0},
        {3, "王五", 92.5},
        {4, "赵六", 78.5},
    };
    int n = sizeof(students) / sizeof(students[0]);

    // 写入记录数
    fwrite(&n, sizeof(int), 1, fp);

    // fwrite:按块写入二进制数据
    // fwrite(数据指针, 每块大小, 块数, 文件指针)
    fwrite(students, sizeof(Student), n, fp);

    fclose(fp);
    printf("写入 %d 条学生记录(二进制格式)\n", n);
    printf("文件大小: %zu 字节\n", sizeof(int) + n * sizeof(Student));
}

void demo_binary_read(void) {
    printf("\n=== 读取二进制文件 ===\n");

    FILE *fp = fopen(TEMP_BIN, "rb");
    if (fp == NULL) {
        fprintf(stderr, "打开二进制文件失败\n");
        return;
    }

    // 读取记录数
    int n;
    fread(&n, sizeof(int), 1, fp);
    printf("共 %d 条记录:\n", n);

    // fread:按块读取
    Student *students = (Student*)malloc(n * sizeof(Student));
    if (students == NULL) {
        fclose(fp);
        return;
    }

    size_t read = fread(students, sizeof(Student), n, fp);
    printf("实际读取 %zu\n", read);

    for (int i = 0; i < (int)read; i++) {
        printf("  [%d] %s: %.1f\n", students[i].id, students[i].name, students[i].score);
    }

    free(students);
    fclose(fp);
}

// ============================================================
//                      文件定位与随机访问
// ============================================================

void demo_seek(void) {
    printf("\n=== 文件定位(随机访问)===\n");

    FILE *fp = fopen(TEMP_BIN, "rb");
    if (fp == NULL) return;

    // ftell:获取当前位置
    printf("初始位置: %ld\n", ftell(fp));

    // fseek:移动文件指针
    // SEEK_SET:从头,SEEK_CUR:从当前,SEEK_END:从尾
    fseek(fp, sizeof(int), SEEK_SET);  // 跳过记录数字段

    // 直接读取第3条记录(索引2)
    int record_index = 2;
    fseek(fp, sizeof(int) + record_index * sizeof(Student), SEEK_SET);
    printf("跳转到第%d条记录,位置: %ld\n", record_index + 1, ftell(fp));

    Student s;
    fread(&s, sizeof(Student), 1, fp);
    printf("第%d条: ID=%d, 姓名=%s, 分数=%.1f\n",
           record_index + 1, s.id, s.name, s.score);

    // 获取文件大小
    fseek(fp, 0, SEEK_END);
    long file_size = ftell(fp);
    printf("文件大小: %ld 字节\n", file_size);

    fclose(fp);
}

// ============================================================
//                      标准流
// ============================================================

void demo_stdio(void) {
    printf("\n=== 标准流 ===\n");

    // stdin:标准输入
    // stdout:标准输出
    // stderr:标准错误(不缓冲,立即输出)

    // 写到 stderr(错误信息)
    fprintf(stderr, "这是错误信息(写到 stderr)\n");

    // fflush:强制刷新缓冲区
    printf("刷新缓冲区中...");
    fflush(stdout);
    printf("完成\n");

    // getchar / putchar(单字符 I/O)
    // printf("请输入一个字符: ");
    // int ch = getchar();
    // printf("你输入了: %c\n", ch);
}

// ============================================================
//                      主函数
// ============================================================

int main(void) {
    printf("=== C 文件 I/O 演示 ===\n");

    // 文本文件
    demo_text_write();
    demo_text_read();

    // 二进制文件
    demo_binary_write();
    demo_binary_read();

    // 文件定位
    demo_seek();

    // 标准流
    demo_stdio();

    // 清理临时文件
    remove(TEMP_FILE);
    remove(TEMP_BIN);
    printf("\n临时文件已清理\n");

    // ----------------------------------------------------------
    // 文件操作最佳实践
    // ----------------------------------------------------------
    printf("\n=== 最佳实践 ===\n");
    printf("1. 始终检查 fopen 返回值是否为 NULL\n");
    printf("2. 始终在 return 前 fclose 文件\n");
    printf("3. 文本模式用 fgets(避免 gets:缓冲区溢出危险)\n");
    printf("4. 二进制模式用 fread/fwrite\n");
    printf("5. 用 ferror 检查读写错误,feof 检查文件末尾\n");
    printf("6. 大文件操作用 fseek/ftell 实现随机访问\n");

    printf("\n=== 文件 I/O 演示完成 ===\n");
    return 0;
}

💬 讨论

使用 GitHub 账号登录后即可参与讨论

基于 MIT 许可发布