寻求适配二进制图书管理需求的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
相关产品推荐
相关产品推荐

