You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

寻求适配二进制图书管理需求的fopen文件打开模式

解决二进制图书文件读写的fopen模式问题

核心思路是分情况处理文件打开方式,同时结合内存操作简化逻辑,具体步骤如下:

1. 处理文件不存在的场景

先尝试用r+b模式打开文件:

  • r+b模式要求文件必须存在,支持读写操作,如果打开失败(fopen返回NULL),说明文件不存在。
  • 此时改用w+b模式创建并打开文件:w+b会创建新文件,同时支持读写,刚好满足初始录入图书信息并写入的需求。

2. 处理文件已存在的场景

文件存在时,先把所有图书数据读取到内存中的结构体数组,所有删除、更新、新增操作都在内存里完成,最后再统一写入文件:

  • 读取完成后可以先关闭文件,操作完内存数组后,用w+b模式重新打开文件(该模式会清空原有内容),再把整个数组写回去,这样就实现了覆盖,不会出现追加写入的问题。
  • 也可以不关闭文件,用fseek(fp, 0, SEEK_SET)将文件指针移到开头,再调用ftruncate(fileno(fp), 0)截断文件(清空内容),然后写入更新后的数组,效果相同。

代码示例片段

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

#define MAX_BOOKS 100

typedef struct {
    char title[50];
    char author[50];
} Book;

int main() {
    FILE *fp;
    Book books[MAX_BOOKS];
    int book_count = 0;

    // 先尝试以读写模式打开现有文件
    fp = fopen("books.dat", "r+b");
    if (fp == NULL) {
        // 文件不存在,创建新文件并录入信息
        fp = fopen("books.dat", "w+b");
        if (!fp) {
            perror("创建文件失败");
            exit(EXIT_FAILURE);
        }

        printf("文件不存在,请录入图书信息(最多%d本,输入空标题结束):\n", MAX_BOOKS);
        while (book_count < MAX_BOOKS) {
            printf("第%d本图书标题: ", book_count + 1);
            fgets(books[book_count].title, sizeof(books[book_count].title), stdin);
            // 去掉换行符
            books[book_count].title[strcspn(books[book_count].title, "\n")] = '\0';
            if (strlen(books[book_count].title) == 0) break;

            printf("作者: ");
            fgets(books[book_count].author, sizeof(books[book_count].author), stdin);
            books[book_count].author[strcspn(books[book_count].author, "\n")] = '\0';

            book_count++;
        }
        // 写入文件
        fwrite(books, sizeof(Book), book_count, fp);
    } else {
        // 读取现有图书到内存数组
        book_count = fread(books, sizeof(Book), MAX_BOOKS, fp);
        fclose(fp); // 先关闭,后续操作完再重新写入

        // 操作菜单
        int choice;
        do {
            printf("\n当前图书数量: %d\n", book_count);
            printf("1. 保留现有图书\n");
            printf("2. 删除图书\n");
            printf("3. 更新图书\n");
            printf("4. 进入新增环节\n");
            printf("请选择操作: ");
            scanf("%d", &choice);
            getchar(); // 清除输入缓冲区的换行符

            switch (choice) {
                case 2: {
                    int del_idx;
                    printf("输入要删除的图书序号(1-%d): ", book_count);
                    scanf("%d", &del_idx);
                    getchar();
                    if (del_idx < 1 || del_idx > book_count) {
                        printf("序号无效\n");
                        break;
                    }
                    // 前移元素实现删除
                    for (int i = del_idx - 1; i < book_count - 1; i++) {
                        books[i] = books[i + 1];
                    }
                    book_count--;
                    printf("删除成功\n");
                    break;
                }
                case 3: {
                    int upd_idx;
                    printf("输入要更新的图书序号(1-%d): ", book_count);
                    scanf("%d", &upd_idx);
                    getchar();
                    if (upd_idx < 1 || upd_idx > book_count) {
                        printf("序号无效\n");
                        break;
                    }
                    printf("新标题: ");
                    fgets(books[upd_idx - 1].title, sizeof(books[upd_idx - 1].title), stdin);
                    books[upd_idx - 1].title[strcspn(books[upd_idx - 1].title, "\n")] = '\0';
                    printf("新作者: ");
                    fgets(books[upd_idx - 1].author, sizeof(books[upd_idx - 1].author), stdin);
                    books[upd_idx - 1].author[strcspn(books[upd_idx - 1].author, "\n")] = '\0';
                    printf("更新成功\n");
                    break;
                }
                case 1:
                    printf("已保留现有图书\n");
                    break;
                case 4:
                    break;
                default:
                    printf("无效选项,请重新选择\n");
            }
        } while (choice != 4);

        // 新增图书直到达到上限
        while (book_count < MAX_BOOKS) {
            printf("\n新增图书标题(输入空结束): ");
            fgets(books[book_count].title, sizeof(books[book_count].title), stdin);
            books[book_count].title[strcspn(books[book_count].title, "\n")] = '\0';
            if (strlen(books[book_count].title) == 0) break;

            printf("作者: ");
            fgets(books[book_count].author, sizeof(books[book_count].author), stdin);
            books[book_count].author[strcspn(books[book_count].author, "\n")] = '\0';

            book_count++;
        }

        // 覆盖写入更新后的图书列表
        fp = fopen("books.dat", "w+b");
        if (!fp) {
            perror("打开文件写入失败");
            exit(EXIT_FAILURE);
        }
        fwrite(books, sizeof(Book), book_count, fp);
    }

    fclose(fp);
    printf("操作完成\n");
    return 0;
}

关键注意点

  • 优先在内存中处理所有数据修改,比直接操作文件指针更直观,避免了a+b模式只能追加、r+b无法创建文件的问题。
  • 使用w+b写入最终数据时,会清空原有文件内容,确保写入的是最新的完整图书列表,不会残留旧数据。

内容的提问来源于stack exchange,提问作者user9026

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 10:36:59