C语言实现简易书籍管理系统

一、引言:为什么需要书籍管理系统?

在日常生活中,我们常常需要整理自己的书籍收藏:可能是个人阅读清单、家庭藏书目录,或是小型图书馆的管理需求。手动记录书籍信息(如书名、作者、类别)容易出错,且查询效率低下。今天,我们将用C语言实现一个简易书籍管理系统,通过结构化的数据存储和友好的用户交互,解决这一痛点。

这个系统将实现以下核心功能:

  • 存储书籍的基础信息(编号、书名、作者、类别);
  • 按类别快速筛选书籍;
  • 清晰展示所有书籍信息;
  • 提供用户友好的交互界面。

通过这个项目,你将掌握C语言中枚举、结构体、函数封装、用户输入处理等核心技术的实际应用。


二、核心数据结构:用枚举和结构体组织信息

1.1 枚举类型:定义书籍类别

书籍的类别是固定的(如科幻、文学、历史等),使用枚举(enum)可以避免魔法数字,提高代码可读性。在头文件function.h中定义如下:

1
2
3
4
5
6
7
typedef enum Genre {
SCIENCE_FICTION = 0, // 科幻
LITERATURE = 1, // 文学
HISTORY = 2, // 历史
TECHNOLOGY = 3, // 科技
OTHER = 4 // 其他
} Genre;

1.2 结构体:封装书籍信息

每本书的信息可以封装为一个结构体(struct),将相关数据绑定在一起。结构体Book包含编号、书名、作者和类别四个字段:

1
2
3
4
5
6
typedef struct Book {
int num; // 编号(唯一标识)
char name[15]; // 书名(最多14字符+终止符)
char author[20]; // 作者(最多19字符+终止符)
Genre genre; // 类别(使用枚举类型)
} Book;

1.3 初始化书籍数组

为了演示功能,我们在源文件main.c中预先初始化一个包含10本书的数组:

1
2
3
4
5
6
7
8
9
10
11
12
Book books[MAX_BOOKS] = {
{1, "三体", "刘慈欣", SCIENCE_FICTION},
{2, "红楼梦", "曹雪芹", LITERATURE},
{3, "中国通史", "吕思勉", HISTORY},
{4, "时间简史", "史蒂芬_霍金", TECHNOLOGY},
{5, "围城", "钱钟书", LITERATURE},
{6, "傲慢与偏见", "简_奥斯汀", LITERATURE},
{7, "呼啸山庄", "艾米莉_勃朗特", LITERATURE},
{8, "活着", "余华", LITERATURE},
{9, "明朝那些事儿", "当年明月", HISTORY},
{10, "乌合之众", "古斯塔夫_勒庞", OTHER}
};

三、功能实现:从数据打印到类别筛选

3.1 函数1:Genre_Zn——枚举转中文

枚举值(如SCIENCE_FICTION)是数字,直接打印不直观。通过switch语句将其转换为中文字符串,增强可读性:

1
2
3
4
5
6
7
8
9
10
const char *Genre_Zn(Genre i) {
switch (i) {
case SCIENCE_FICTION: return "科幻";
case LITERATURE: return "文学";
case HISTORY: return "历史";
case TECHNOLOGY: return "科技";
case OTHER: return "其他";
default: return "未知"; // 处理非法输入
}
}

3.2 函数2:print_book_info——打印所有书籍信息

该函数遍历书籍数组,按固定格式打印每本书的详细信息。为了美观,使用分隔线增强可读性:

1
2
3
4
5
6
7
8
9
10
11
12
void print_book_info(Book *books, int count) {
// 打印分隔线(21个"-")
for (int i = 0; i < 21; i++) printf("-");
printf(" 所有的书籍信息 ");
for (int i = 0; i < 21; i++) printf("-");
printf("\n");

// 遍历并打印每本书
for (int i = 0; i < MAX_BOOKS; i++) {
printf("编号:%-2d 书名:%-12s 作者:%-13s 类别:%-8s\n",books[i].num, books[i].name,books[i].author,Genre_Zn(books[i].genre));
}
}

3.3 函数3:find_books_by_genre——按类别筛选书籍

用户输入类别编号后,该函数遍历数组,只打印符合条件的书籍:

1
2
3
4
5
6
7
void find_books_by_genre(Book *books, int count, Genre genre) {
for (int i = 0; i < count; i++) {
if (books[i].genre == genre) {
printf("编号:%-2d 书名:%-12s 作者:%-13s 类别:%-8s\n",books[i].num, books[i].name,books[i].author,Genre_Zn(books[i].genre));
}
}
}

四、主函数:用户交互流程

主函数负责调用上述函数,提供交互界面。用户可以查看所有书籍,或按类别筛选书籍,输入5退出程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int main(void) {
// 打印所有书籍信息
print_book_info(books, MAX_BOOKS);

// 用户按类别查询循环
Genre gen;
do {
printf("\n请输入书籍类别编号(0:科幻 1.文学 2.历史 3.科技 4.其他 5.退出)\n");
scanf("%d", &gen); // 读取用户输入的类别编号
find_books_by_genre(books, MAX_BOOKS, gen); // 筛选并打印
} while (gen != 5); // 输入5时退出循环

return 0;
}

五、测试与验证:运行效果展示

5.1 初始书籍列表打印

程序启动后,首先打印所有书籍的信息,输出类似:

1
2
3
4
5
6
7
8
9
10
11
12
--------------------- 所有的书籍信息 ---------------------

编号:1 书名:三体 作者:刘慈欣 类别:科幻
编号:2 书名:红楼梦 作者:曹雪芹 类别:文学
编号:3 书名:中国通史 作者:吕思勉 类别:历史
编号:4 书名:时间简史 作者:史蒂芬_霍金 类别:科技
编号:5 书名:围城 作者:钱钟书 类别:文学
编号:6 书名:傲慢与偏见 作者:简_奥斯汀 类别:文学
编号:7 书名:呼啸山庄 作者:艾米莉_勃朗特 类别:文学
编号:8 书名:活着 作者:余华 类别:文学
编号:9 书名:明朝那些事儿 作者:当年明月 类别:历史
编号:10 书名:乌合之众 作者:古斯塔夫_勒庞 类别:其他

5.2 按类别查询功能

用户输入1(文学),程序筛选并打印所有文学类书籍:

1
2
3
4
5
编号:2  书名:红楼梦      作者:曹雪芹       类别:文学    
编号:5 书名:围城 作者:钱钟书 类别:文学
编号:6 书名:傲慢与偏见 作者:简_奥斯汀 类别:文学
编号:7 书名:呼啸山庄 作者:艾米莉_勃朗特 类别:文学
编号:8 书名:活着 作者:余华 类别:文学

输入5后,程序退出。


六、潜在问题

问题:MAX_BOOKS未定义

当前代码中MAX_BOOKSfunction.h中定义为10,但需确保源文件main.cfunction.c中一致。若书籍数量超过MAX_BOOKS,会导致数组越界。需要在function.h中明确定义,并在使用时检查边界。

改进建议1:增加书籍属性

可以扩展Book结构体,添加出版年份、ISBN、页数等字段,丰富信息维度:

1
2
3
4
5
6
7
8
typedef struct Book {
int num; // 编号
char name[15]; // 书名
char author[20]; // 作者
Genre genre; // 类别
int publish_year; // 出版年份(新增)
char isbn[13]; // ISBN(新增)
} Book;

改进建议2:数据持久化

当前书籍数据是硬编码的,重启程序后数据丢失。可以将数据保存到文件(如books.txt),启动时读取,实现持久化:

1
2
3
4
5
6
7
8
9
// 示例:从文件读取书籍数据
FILE *fp = fopen("books.txt", "r");
if (fp) {
fscanf(fp, "%d", &count); // 读取书籍数量
for (int i = 0; i < count; i++) {
fscanf(fp, "%d %s %s %d", &books[i].num, books[i].name, books[i].author, &books[i].genre);
}
fclose(fp);
}

改进建议3:图形化界面

当前交互是命令行形式,可通过SDLQt等库开发图形界面(GUI),提升用户体验。


七、总结:从代码到能力的提升

通过这个简易书籍管理系统,我们实践了C语言的核心知识点:

  • 枚举类型:定义固定类别,提高代码可读性;
  • 结构体:封装复杂数据,组织信息更清晰;
  • 函数封装:将功能拆分为独立函数,提升代码复用性;
  • 用户交互:处理输入输出,实现基础的CLI(命令行界面)。

这些经验是后续开发更复杂系统(如学生管理系统、库存管理系统)的基础。不妨尝试扩展本文的功能(如添加书籍、删除书籍),在实践中进一步提升编程能力!


八、完整源代码

头文件 function.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#ifndef FUNCTION_H
#define FUNCTION_H
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdbool.h>
#define MAX_BOOKS 10

typedef enum Genre {
SCIENCE_FICTION = 0,
LITERATURE = 1,
HISTORY = 2,
TECHNOLOGY = 3,
OTHER = 4
} Genre;

typedef struct Book {
int num;
char name[15];
char author[20];
Genre genre;
} Book;

const char *Genre_Zn(Genre i);
void print_book_info(Book *books, int count);
void find_books_by_genre(Book *books, int count, Genre genre);
#endif

源文件 main.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdbool.h>
#include "function.h"

Book books[MAX_BOOKS] = {
{1, "三体", "刘慈欣", SCIENCE_FICTION},
{2, "红楼梦", "曹雪芹", LITERATURE},
{3, "中国通史", "吕思勉", HISTORY},
{4, "时间简史", "史蒂芬_霍金", TECHNOLOGY},
{5, "围城", "钱钟书", LITERATURE},
{6, "傲慢与偏见", "简_奥斯汀", LITERATURE},
{7, "呼啸山庄", "艾米莉_勃朗特", LITERATURE},
{8, "活着", "余华", LITERATURE},
{9, "明朝那些事儿", "当年明月", HISTORY},
{10, "乌合之众", "古斯塔夫_勒庞", OTHER}
};

int main(void) {
print_book_info(books, MAX_BOOKS);
Genre gen;
do {
printf("\n请输入书籍类别编号(0:科幻 1.文学 2.历史 3.科技 4.其他 5.退出)\n");
scanf("%d", &gen);
find_books_by_genre(books, MAX_BOOKS, gen);
} while (gen != 5);
return 0;
}

源文件 function.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdbool.h>
#include "function.h"

const char *Genre_Zn(Genre i) {
switch (i) {
case SCIENCE_FICTION: return "科幻";
case LITERATURE: return "文学";
case HISTORY: return "历史";
case TECHNOLOGY: return "科技";
case OTHER: return "其他";
default: return "未知";
}
}

void print_book_info(Book *books, int count) {
for (int i = 0; i < 21; i++) printf("-");
printf(" 所有的书籍信息 ");
for (int i = 0; i < 21; i++) printf("-");
printf("\n");

for (int i = 0; i < MAX_BOOKS; i++) {
printf("编号:%-2d 书名:%-12s 作者:%-13s 类别:%-8s\n", books[i].num, books[i].name, books[i].author, Genre_Zn(books[i].genre));
}
}

void find_books_by_genre(Book *books, int count, Genre genre) {
for (int i = 0; i < count; i++) {
if (books[i].genre == genre) {
printf("编号:%-2d 书名:%-12s 作者:%-13s 类别:%-8s\n", books[i].num, books[i].name, books[i].author, Genre_Zn(books[i].genre));
}
}
}