C语言图书管理系统实验报告
| 实验名称 | C语言图书管理系统设计与实现 | 实验日期 | 2025年10月27日 |
|---|---|---|---|
| 学生姓名 | [你的姓名] | 学 号 | [你的学号] |
| 指导教师 | [教师姓名] | 班 级 | [你的班级] |
实验目的
- 巩固C语言基础知识:熟练掌握C语言的基本语法,包括数据类型、运算符、流程控制语句(if-else, switch, for, while)等。
- 掌握核心数据结构:深入理解和应用结构体(
struct)来组织复杂数据,并熟练运用文件指针(FILE*)进行数据的持久化存储与读取。 - 提升模块化编程能力:学习将一个复杂系统分解为若干个功能独立、逻辑清晰的函数,实现高内聚、低耦合的代码结构。
- 培养问题分析与解决能力:通过设计并实现一个完整的图书管理系统,锻炼从需求分析、系统设计到编码实现、测试调试的全过程能力。
- 理解用户交互设计:学习设计简单、友式的命令行用户界面,提升程序的可用性。
实验环境
- 硬件环境:Intel Core i5 处理器, 8GB 内存
- 软件环境:
- 操作系统:Windows 11
- 开发工具:Visual Studio Code 1.85
- 编译器:MinGW-w64 (GCC 13.2.0)
- 终端:Windows Terminal
需求分析
本系统旨在模拟一个小型图书馆的日常图书管理操作,根据基本需求,系统需要具备以下核心功能:
- 图书信息录入:能够将新图书的信息(如ISBN、书名、作者、库存数量)添加到系统中。
- 图书信息浏览:能够以列表形式展示系统中所有图书的详细信息。
- 图书信息查询:提供多种查询方式,如按ISBN、按书名、按作者进行精确或模糊查询。
- 图书信息修改:能够根据ISBN查找到特定图书,并修改其信息(如书名、作者、库存等)。
- 图书信息删除:能够根据ISBN从系统中删除某本图书。
- 数据持久化:系统的图书数据需要保存在文件中,确保程序关闭后再次打开时,数据依然存在。
系统设计
1 功能模块设计
根据需求分析,将系统划分为以下几个主要功能模块,每个模块由一个或多个函数实现。
| 模块名称 | 功能描述 | 主要函数 |
|---|---|---|
| 主菜单模块 | 显示系统主菜单,接收用户输入并调用相应功能模块。 | main() |
| 录入模块 | 添加新图书信息到内存和文件中。 | addBook() |
| 浏览模块 | 显示所有图书的详细信息。 | displayAllBooks() |
| 查询模块 | 提供多种查询方式并显示结果。 | searchBook() |
| 修改模块 | 根据ISBN查找并修改图书信息。 | modifyBook() |
| 删除模块 | 根据ISBN查找并删除图书。 | deleteBook() |
| 文件操作模块 | 负责程序启动时从文件加载数据,退出时保存数据到文件。 | loadData(), saveData() |
2 数据结构设计
为了存储图书信息,设计一个名为 Book 的结构体类型。
// 图书信息结构体
typedef struct {
char isbn[20]; // ISBN编号,作为唯一标识
char title[100]; // 书名
char author[50]; // 作者
int quantity; // 库存数量
} Book;
3 文件存储设计
为了实现数据持久化,使用文本文件 books.dat 来存储所有图书信息,文件格式采用每行存储一本图书的信息,字段之间用逗号()分隔,
978-7-111-54321-0,C Primer Plus,Stephen Prata,15
978-7-302-12345-6,C程序设计,谭浩强,30
978-7-115-67890-1,数据结构,严蔚敏,20
详细设计与实现
1 核心函数设计
-
*`void addBook(Book books[], int count)`**
- 功能:向
books数组中添加一本新书。 - 实现:提示用户输入图书的ISBN、书名、作者和库存,首先检查ISBN是否已存在,若存在则提示更新库存;若不存在,则将新书信息追加到数组末尾,并更新图书总数
*count,最后调用saveData()保存到文件。
- 功能:向
-
void displayAllBooks(Book books[], int count)- 功能:以表格形式展示所有图书。
- 实现:首先打印表头(如“序号”、“ISBN”、“书名”、“作者”、“库存”),然后遍历
books数组,逐行打印每本图书的信息。count为0,则提示“图书馆为空”。
-
void searchBook(Book books[], int count)- 功能:提供菜单,让用户选择按ISBN、书名或作者进行查询。
- 实现:内部定义
searchByIsbn(),searchByTitle(),searchByAuthor()三个子函数,主函数接收用户选择,并调用相应的子函数,查询逻辑为遍历数组,进行字符串比较(strcmp)。
-
void modifyBook(Book books[], int count)- 功能:根据ISBN修改图书信息。
- 实现:提示用户输入要修改的ISBN,遍历数组查找,若找到,提示用户输入新的书名、作者和库存,并更新该图书的结构体成员,如果未找到,则提示“未找到该图书”,修改成功后调用
saveData()。
-
*`void deleteBook(Book books[], int count)`**
- 功能:根据ISBN删除图书。
- 实现:提示用户输入要删除的ISBN,遍历数组查找,若找到,将该图书之后的所有图书向前移动一位,覆盖掉要删除的图书,然后将图书总数
*count减1,如果未找到,则提示“未找到该图书”,删除成功后调用saveData()。
-
*`void loadData(Book books[], int count)`**
- 功能:程序启动时从
books.dat文件加载数据。 - 实现:以读模式(
"r")打开文件,使用fscanf逐行读取文件内容,将解析出的数据存入books数组,并更新*count,如果文件不存在,则不执行任何操作(首次运行)。
- 功能:程序启动时从
-
void saveData(Book books[], int count)- 功能:将内存中的图书数据保存到
books.dat文件。 - 实现:以写模式(
"w")打开文件(会清空原文件内容),遍历books数组,使用fprintf将每本图书的信息按预定格式写入文件,最后关闭文件。
- 功能:将内存中的图书数据保存到
2 主程序流程图
graph TD
A[开始] --> B{程序启动};
B --> C[loadData()];
C --> D[显示主菜单];
D --> E{用户输入选择};
E -- 1. 录入 --> F[addBook()];
E -- 2. 浏览 --> G[displayAllBooks()];
E -- 3. 查询 --> H[searchBook()];
E -- 4. 修改 --> I[modifyBook()];
E -- 5. 删除 --> J[deleteBook()];
E -- 0. 退出 --> K[saveData()];
K --> L[显示“感谢使用”];
L --> M[结束];
F --> D;
G --> D;
H --> D;
I --> D;
J --> D;
系统测试
为了验证系统的正确性和稳定性,设计了以下测试用例。
| 测试编号 | 测试步骤 | 预期结果 | 实际结果 | 是否通过 |
|---|---|---|---|---|
| TC-01 | 启动程序。 | 显示“图书馆为空”或空列表。 | 显示“图书馆为空”。 | 通过 |
| TC-02 | 选择“1. 录入图书”。 输入ISBN: 978-7-111-12345-6, 书名: C语言, 作者: 作者A, 库存: 10。 |
提示“添加成功!”,在浏览列表中能看到新添加的图书。 | 提示“添加成功!”,浏览列表显示新图书。 | 通过 |
| TC-03 | 重复TC-02,输入相同的ISBN。 | 提示“该图书已存在,是否更新库存?”,选择“是”后库存相加。 | 提示“该图书已存在,是否更新库存?”,输入“Y”后库存变为20。 | 通过 |
| TC-04 | 选择“3. 查询图书”。 选择“2. 按书名查询”。 输入: C语言。 |
显示所有书名为“C语言”的图书信息。 | 正确显示“C语言”这本书的信息。 | 通过 |
| TC-05 | 选择“4. 修改图书”。 输入ISBN: 978-7-111-12345-6。 修改作者为“作者B”。 |
提示“修改成功!”,再次查询该书,作者已更新为“作者B”。 | 提示“修改成功!”,查询结果确认作者已更新。 | 通过 |
| TC-06 | 选择“5. 删除图书”。 输入ISBN: 978-7-111-12345-6。 |
提示“删除成功!”,再次浏览,该书已不存在。 | 提示“删除成功!”,浏览列表中已无该书。 | 通过 |
| TC-07 | 选择“5. 删除图书”。 输入一个不存在的ISBN。 |
提示“未找到该图书,删除失败!”。 | 提示“未找到该图书,删除失败!”。 | 通过 |
| TC-08 | 完成所有操作后,选择“0. 退出”。 重新启动程序。 |
程序退出后重新启动,之前添加、修改的图书数据依然存在。 | 数据成功恢复,与退出前一致。 | 通过 |
实验总结与心得
通过本次实验,我成功地设计并实现了一个功能完善的C语言图书管理系统,在实践过程中,我获得了以下几点收获与体会:
- 理论与实践的结合:本次实验将课堂上学习的C语言理论知识,如结构体、文件操作、函数等,应用于解决一个实际问题,极大地加深了我对这些知识点的理解和记忆。
- 模块化编程的重要性:将系统划分为多个功能模块,使得代码结构清晰,易于编写、阅读和维护,所有文件操作都集中在
loadData和saveData函数中,当需要修改存储格式时,只需改动这两个函数,而不会影响其他业务逻辑。 - 细节决定成败:在编码过程中,许多细节需要特别注意,
- 边界条件:数组访问时防止越界,文件打开失败时的处理。
- 数据唯一性:在添加和修改时,确保ISBN的唯一性是关键。
- 用户体验:提供清晰的菜单和提示信息,能让用户更方便地使用程序。
- 调试能力的提升:在实现查询、删除等功能时,遇到了一些逻辑错误,例如删除图书后数组元素没有正确前移,导致数据错乱,通过使用
printf进行打印调试,逐步定位并解决了问题,这锻炼了我的调试和排错能力。
存在的不足与展望:
- 功能扩展性:当前系统功能相对基础,未来可以增加借阅、归还、用户管理等功能,使其成为一个更完整的图书馆管理系统。
- 数据结构优化:对于图书数量巨大的情况,使用线性查找效率较低,未来可以引入链表或哈希表等更高效的数据结构来优化查询、插入和删除操作。
- 界面美化:当前是纯命令行界面,可以增加颜色、进度条等,使界面更美观。
本次实验是一次宝贵的实践经历,不仅巩固了我的C语言编程技能,也培养了我分析问题、设计解决方案和动手实现的综合能力。
核心代码实现
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX_BOOKS 1000
#define FILENAME "books.dat"
// 图书信息结构体
typedef struct {
char isbn[20];
char title[100];
char author[50];
int quantity;
} Book;
// 函数声明
void showMenu();
void addBook(Book books[], int *count);
void displayAllBooks(Book books[], int count);
void searchBook(Book books[], int count);
void modifyBook(Book books[], int count);
void deleteBook(Book books[], int *count);
void loadData(Book books[], int *count);
void saveData(Book books[], int count);
int main() {
Book books[MAX_BOOKS];
int bookCount = 0;
int choice;
// 启动时加载数据
loadData(books, &bookCount);
do {
showMenu();
printf("请输入您的选择: ");
scanf("%d", &choice);
while (getchar() != '\n'); // 清空输入缓冲区
switch (choice) {
case 1:
addBook(books, &bookCount);
break;
case 2:
displayAllBooks(books, bookCount);
break;
case 3:
searchBook(books, bookCount);
break;
case 4:
modifyBook(books, bookCount);
break;
case 5:
deleteBook(books, &bookCount);
break;
case 0:
printf("正在保存数据并退出系统...\n");
saveData(books, bookCount);
printf("感谢使用,再见!\n");
break;
default:
printf("无效的选择,请重新输入!\n");
}
} while (choice != 0);
return 0;
}
// 显示主菜单
void showMenu() {
printf("\n========== 图书馆管理系统 ==========\n");
printf(" 1. 录入图书\n");
printf(" 2. 浏览图书\n");
printf(" 3. 查询图书\n");
printf(" 4. 修改图书\n");
printf(" 5. 删除图书\n");
printf(" 0. 退出系统\n");
printf("====================================\n");
}
// 录入图书
void addBook(Book books[], int *count) {
if (*count >= MAX_BOOKS) {
printf("图书馆已满,无法添加更多图书!\n");
return;
}
Book newBook;
printf("请输入ISBN: ");
scanf("%19s", newBook.isbn);
while (getchar() != '\n'); // 清空输入缓冲区
// 检查ISBN是否已存在
for (int i = 0; i < *count; i++) {
if (strcmp(books[i].isbn, newBook.isbn) == 0) {
printf("该图书已存在,是否更新库存?(y/n): ");
char ch;
scanf(" %c", &ch);
if (ch == 'y' || ch == 'Y') {
printf("请输入增加的库存数量: ");
int addQty;
scanf("%d", &addQty);
books[i].quantity += addQty;
printf("库存更新成功!\n");
saveData(books, *count);
}
return;
}
}
printf("请输入书名: ");
fgets(newBook.title, sizeof(newBook.title), stdin);
newBook.title[strcspn(newBook.title, "\n")] = 0; // 去除换行符
printf("请输入作者: ");
fgets(newBook.author, sizeof(newBook.author), stdin);
newBook.author[strcspn(newBook.author, "\n")] = 0;
printf("请输入库存数量: ");
scanf("%d", &newBook.quantity);
books[*count] = newBook;
(*count)++;
printf("图书添加成功!\n");
saveData(books, *count);
}
// 浏览所有图书
void displayAllBooks(Book books[], int count) {
if (count == 0) {
printf("图书馆为空,没有图书可显示,\n");
return;
}
printf("\n%-5s %-20s %-30s %-20s %-10s\n", "序号", "ISBN", "书名", "作者", "库存");
printf("--------------------------------------------------------------------------------\n");
for (int i = 0; i < count; i++) {
printf("%-5d %-20s %-30s %-20s %-10d\n", i + 1, books[i].isbn, books[i].title, books[i].author, books[i].quantity);
}
}
// 查询图书 (示例:实现按ISBN查询)
void searchBook(Book books[], int count) {
if (count == 0) {
printf("图书馆为空,无法查询,\n");
return;
}
printf("请输入要查询的ISBN: ");
char isbn[20];
scanf("%19s", isbn);
int found = 0;
printf("\n查询结果:\n");
printf("%-20s %-30s %-20s %-10s\n", "ISBN", "书名", "作者", "库存");
printf("------------------------------------------------------------\n");
for (int i = 0; i < count; i++) {
if (strstr(books[i].isbn, isbn) != NULL) { // 模糊查询
printf("%-20s %-30s %-20s %-10d\n", books[i].isbn, books[i].title, books[i].author, books[i].quantity);
found = 1;
}
}
if (!found) {
printf("未找到ISBN包含 \"%s\" 的图书,\n", isbn);
}
}
// 修改图书
void modifyBook(Book books[], int count) {
if (count == 0) {
printf("图书馆为空,无法修改,\n");
return;
}
printf("请输入要修改的图书ISBN: ");
char isbn[20];
scanf("%19s", isbn);
for (int i = 0; i < count; i++) {
if (strcmp(books[i].isbn, isbn) == 0) {
printf("找到图书,请输入新的信息:\n");
printf("请输入新书名: ");
fgets(books[i].title, sizeof(books[i].title), stdin);
books[i].title[strcspn(books[i].title, "\n")] = 0;
printf("请输入新作者: ");
fgets(books[i].author, sizeof(books[i].author), stdin);
books[i].author[strcspn(books[i].author, "\n")] = 0;
printf("请输入新库存数量: ");
scanf("%d", &books[i].quantity);
printf("图书信息修改成功!\n");
saveData(books, count);
return;
}
}
printf("未找到ISBN为 \"%s\" 的图书,\n", isbn);
}
// 删除图书
void deleteBook(Book books[], int *count) {
if (*count == 0) {
printf("图书馆为空,无法删除,\n");
return;
}
printf("请输入要删除的图书ISBN: ");
char isbn[20];
scanf("%19s", isbn);
for (int i = 0; i < *count; i++) {
if (strcmp(books[i].isbn, isbn) == 0) {
// 将后面的元素前移
for (int j = i; j < *count - 1; j++) {
books[j] = books[j + 1];
}
(*count)--;
printf("图书删除成功!\n");
saveData(books, *count);
return;
}
}
printf("未找到ISBN为 \"%s\" 的图书,\n", isbn);
}
// 从文件加载数据
void loadData(Book books[], int *count) {
FILE *fp = fopen(FILENAME, "r");
if (fp == NULL) {
// 文件不存在,首次运行
return;
}
*count = 0;
while (fscanf(fp, "%19[^,],%99[^,],%49[^,],%d\n", books[*count].isbn, books[*count].title, books[*count].author, &books[*count].quantity) == 4) {
(*count)++;
if (*count >= MAX_BOOKS) break;
}
fclose(fp);
}
// 保存数据到文件
void saveData(Book books[], int count) {
FILE *fp = fopen(FILENAME, "w");
if (fp == NULL) {
printf("错误:无法打开文件进行保存!\n");
return;
}
for (int i = 0; i < count; i++) {
fprintf(fp, "%s,%s,%s,%d\n", books[i].isbn, books[i].title, books[i].author, books[i].quantity);
}
fclose(fp);
}
