一、系统功能
图书管理系统要求实现图书管理的基本功能,包括图书的录入、删除、查找和导入/导出等。图书的属性包括书号、书名、第一作者、版次、出版社、出版年等信息。
1.创建:创建图书管理系统,用逐条输入的方式;
2.显示:分屏显示图书管理系统中的所有记录;
3.插入:向图书管理系统中插入一条记录;
4.删除:删除一条已经存在的记录项;
5.排序:以编号为依据升序排列所有记录项;
6.查找:根据用户输入的属性值查找符合条件的记录项;
7.导入/导出:可以从文件读入已有的图书管理系统,也可将系统中的图书信息导出到文件;
8.反序:以编号为依据将按序排列的记录项反序排列;
9.删除相同记录。
构建菜单系统,程序执行过程为:循环显示主菜单,用户在Give your choice:处输入选项,即按照功能列表输入0~10中的任意一个数字,按回车后,执行相应的功能。功能执行完毕,返回菜单。
二、菜单功能
各菜单项功能如下:
(1)Input Records(建立有序表)
从键盘上一次输入一条图书记录(书号、书名、第一作者、版次、出版社、出版年),以“书号”为序建立有序表。插入一条记录后,显示提示信息:确认是否输入下一条记录,如确认,继续输入,否则,退出输入功能。
(2)Display All Records(显示所有结点记录)
按顺序显示表中所有记录,每屏显示10条记录,按
(3)Insert a Record(插入一条结点记录)
在以“书号”为序排列的表中插入一条记录,插入后,表仍有序。输出插入成功的信息。
(4)Delete a Record(按“书号”查找,找到后删除该条结点记录)
输入待删除记录的“书号”,显示提示信息,让用户再次确认是否要删除。确认后,将该“书号”的记录删除。
(5) Sort(排序)
以“书号”为升序排列表中的记录。
(6)Query(查找并显示一个结点记录)
输入“书号”,查找该记录,找到后显示记录信息。
(7)Add Records from a Text File(从文件中添加数据到系统中)
用户可事前建立一个文件data.txt,存放多条待加入的记录。提示输入文件的文件名,然后从该文件中一次性加入多条图书记录。文件data.txt格式如下(可以使用英文或拼音表示中文信息):
(8)Write to a Text File(将系统中记录写入文件)
将图书管理系统中的全部记录写入文件records.txt,要求文件格式和文件data.txt相同。
(9)Reverse List(将表反序)
将表中的所有结点按“书号”反序存放。
(10)Delete the Same Records(删除相同记录)
删除书名、第一作者、版次、出版社、出版年均相同的记录。
(0)Quit(退出图书管理系统程序)
退出图书管理系统程序。
三、程序实现
1. 数据结构
用结构体数组实现图书信息的记录和管理。每个数组元素为一个结构体变量,其结构如下:
在主函数中定义结构体数组Bookinfo books[NUM];用作记录存储。
2. 各主要功能函数
(1)数据输入
int Input(Bookinfo dictList[],int n);
从键盘输入若干条记录,依次存放到结构体数组dictList中,n为数组原有记录数,函数返回最后的记录数。
(2)输出数据
void Diplay(Bookinfo dictList[],int n);
显示所有图书信息,每10本暂停一次,n为数组元素个数。
(3)插入一条结点记录
int Insert(Bookinfo dictList[],int n, Bookinfo *s);
按书号序插入记录s,返回记录个数。
int Insert_a_record(Bookinfo dictList[],int n);
输入待插入的图书书号、书名、作者、版本号、出版社名、出版年等图书信息,调用Insert函数按书号作有序插入,输出插入成功信息,返回记录个数。
(4)删除一条结点记录
int Delete(Bookinfo dictList[],int n, char *book);
删除书名为book的第一条图书记录,返回数组中的记录数。
int Delete_a_record(Bookinfo dictList[],int n);
输入待删除的书名,经确认后调用Delete函数,同时列出同一书名的所有书目,输入待删除书目的书号,提示是否删除,确认后,输出删除成功与否的信息,返回数组中的记录数。
(5)排序
void Sort_by_ISBN(Bookinfo dictList[],int n);
数组按书号升序排列。
(6)查询数据
int Query(Bookinfo dictList[],int n, char *book);
查找并显示书名为book的所有记录,查找成功返回该书名记录个数,否则返回-1。
void Query_a_record(Bookinfo dictList[], int n);
输入待查找的书名,调用Query函数查找该书的记录,输出查找成功与否的信息和该书名的所有记录。
(7)从文件中整批输入数据
int AddfromText(Bookinfo dictList[],int n, char *filename);
从文件filename添加一批记录到数组中,调用Insert()函数作有序插入,返回添加记录后的新记录数。
(8)将记录写到文件
void WriteText(Bookinfo dictList[],int n,char *filename);
将数组中的记录全部写入文件filename中。
(9)将表反序存放
void Reverse(Bookinfo dictList[]);
按书号反序存放表。
(10)删除雷同记录
int DeleteSame(Bookinfo dictList[], int n);
删除数组中书名、作者、版本号、出版社名、出版年均相同的记录,n为数组原有记录数,函数返回最后的记录数。
(0)退出管理系统
void Quit(Bookinfo dictList[]);
退出系统
程序代码如下
#pragma warning(disable:4996) #include#include #include #include #define NUM 100 // * 最大的书籍信息数量 #define NUM1 20 #define NUM2 60 // * 定义结构体 typedef struct { char ISBN[10]; // * 书号 char book[30]; // * 书名 char author[20]; // * 作者 int edition; // * 版本号 char press[50]; // * 出版社名 int year; // * 出版年 } Bookinfo; / n++; while (1) { system("cls"); Beauty(); printf("是否继续输入(y/n):"); judge = getch(); // * 消化换行符 system("cls"); // * 判断是否正确输入 if (judge == 'y' || judge == 'n' || judge == 'Y' || judge == 'N') break; } // * 判断是否退出 if (judge == 'y' || judge == 'Y') continue; return n; } } // * 在翻页的功能上还需要完善一下 // * 完善好了,可以自由翻页了 void Display(Bookinfo dictList[], int n) { system("cls"); int i; int page;// * 记录当前页码 int page_all = n / 10 + 1; // * 记录显示的总页数 if (n == 0) { Beauty(); printf("系统中没有录入图书!n"); system("pause"); return; } // * 防止书的数量是10的倍数出错 if (n % 10 == 0) page_all = n / 10 ; else page_all = n / 10 + 1; OutputBookHead(); // * 输出表头信息 // * 列出书籍信息的功能 for (i = 0; i < n; i++) { OutputBookInformation(dictList[i]); // * 每十本书暂停一次,或者到达末尾暂停一次 if ((i + 1) % 10 == 0 || i == n - 1) { page = (i / 10) + 1; // * 输出页码标识 printf("nn第 %d 页 \ 共 %d 页(一共 %d 条记录)n", page, page_all, n); Space(); printf("按左方向键返回前一页,右方向键来到下页, enter退出显示功能"); switch (getch()) { case 224: //方向键区的ASCII码 switch (getch()) { case 75: if (i >= 19) i -= (i % 10 + 11); else i = -1; break;// * 按左方向键执行 case 77: if (page == page_all) i = (page - 1) * 10 - 1;// * 到达最后一页的i往后退,减1防止后面i++ break;// * 按右方向键执行 default: i -= (i % 10 + 1);// * 输入其他按键不改变原来输入的值,把i归位 } break; case 'r': return;// * 打enter键退出 default: i -= (i % 10 + 1);// * 输入其他按键不改变原来输入的值,把i归位 } system("cls"); OutputBookHead(); } } } // * 插入一条结点记录(按书号插入) // * 返回数组中的记录数 int Insert(Bookinfo dictList[], int n, Bookinfo* s) { int i, j; int judge = 1; // * 判断是否没有比它大的书号 // * 判断要插入的书号是否重复 // * 感觉不要判断重复因为10的功能就是删除重复 // * 插入图书 for (i = 0; i < n; i++) { // * 判断插入条件->书号是刚好小于后面那个 if (strcmp(dictList[i].ISBN, s->ISBN) == 1) { // * 把所有之后的结点后移一位再插入 for (j = n; j > i; j--) { dictList[j] = dictList[j - 1]; } dictList[i] = *s; judge = 0;// * 证明有符合条件的,不用插最后了 break; // * 完成插入了不要循环了 } } // * 如果没插入就放在最后 if (judge) { dictList[n] = *s; } return n + 1; } // * 传入待插入的信息,调用Insert函数插入 int Insert_a_record(Bookinfo dictList[], int n) { system("cls"); int temp = n;// * 缓存n的值以判断是否是相同书号的书籍 Bookinfo book = InputBookInformation(); n = Insert(dictList, n, &book); // * 判断要插入的书号是否重复 // // * 感觉不要判断是否重复插入因为10的功能就是删除重复 return n; } // * 列出符合书名的图书并删除一条结点记录 int Delete(Bookinfo dictList[], int n, char* book) { system("cls"); int i; char ISBN[10];// * 输入书号 int temp = n;// * 储存n原来的值,如果改变了证明删除成功了 // * 输出符合条件的书名的书籍信息 Enter(); OutputBookHead(); for (i = 0; i < n; i++) { if (!strcmp(book, dictList[i].book)) { OutputBookInformation(dictList[i]); } } BigSpace(); printf("nn请输入你要删除的书号:"); gets(ISBN); BigSpace(); if ((n = DeleteByISBNAndName(dictList, n, ISBN, book)) < temp) { printf("删除成功!"); } else printf("删除失败!(原因可能是因为书号输入有误)n"); system("pause"); return n; } // * 输入书名,调用Delete列出,然后选择删除的书号 int Delete_a_record(Bookinfo dictList[], int n) { system("cls"); Beauty(); char bookname[30]; printf("请输入要删除的图书名字:"); gets(bookname); // * 判断数组中是否有相同的图书名 if (!JudgeSameName(dictList, n, bookname)) { printf("nn"); BigSpace(); printf("输入的图书不存在!n"); system("pause"); return n; } n = Delete(dictList, n, bookname); return n; } // * 数组按照书号进行升序排序 void Sort_by_ISBN(Bookinfo dictList[], int n) { system("cls"); int i, j;// * 冒泡排序的循环变量 Bookinfo temp;// * 交换的中间变量 // * 冒泡排序 for (i = 0; i < n - 1; i++) { for (j = 0; j < n - 1 - i; j++) { if (strcmp(dictList[j].ISBN, dictList[j + 1].ISBN) == 1) { temp = dictList[j]; dictList[j] = dictList[j + 1]; dictList[j + 1] = temp; } } } Beauty(); printf("排序完成!一共对 %d 个图书信息进行了排序!n", n); // * 输出排序完成的提示信息 system("pause"); } // * 查询数据 // * 显示book所有的记录,返回其个数(如果没有就返回0) int Query(Bookinfo dictList[], int n, char* book) { int num = 0;// * 计数器 int i; printf("nn"); OutputBookHead(); // * 输入表头 for (i = 0; i < n; i++) { if (!strcmp(dictList[i].book, book)) { OutputBookInformation(dictList[i]); // * 输出图书的信息 num++; } } return num; } // * 输入要查找的书名,调用Query查询记录,输出查找成功与否的信息和该书名的所有记录 void Query_a_record(Bookinfo dicList[], int n) { system("cls"); char bookname[30]; int num;// * 记录搜索到的书籍个数 Beauty(); printf("请输入要查询的书籍名称:"); gets(bookname); // * 判断num是否为0,为0输出没有该书,非0输出书籍个数 if (num = Query(dicList, n, bookname)) { printf("nn搜索到 %d 个相同结果nn", num); } else { system("cls"); Beauty(); printf("书库中没有该书!n"); } system("pause"); } // * 从文件中添加一批数据记录到数组之中,同时调用Insert函数作有序插入 // * 返回记录数量 int AddfromText(Bookinfo dictList[], int n, char* filename) { system("cls"); Beauty();// * 美化界面 FILE* fp; int num = n; // * 记录添加图书的数量,有相同书号不添加 Bookinfo booktemp; // * 用来过渡文件中的数据 // * 打开文件并判断打开是否成功 if ((fp = fopen(filename, "r")) == NULL) { printf("文件打开失败!!!n"); system("pause"); fclose(fp); return n; } while (1) { booktemp = freadBookInformation(fp); // * "end" 是在freadBookInformation 函数里面设置的,为了在文件末尾终止循环 if (!strcmp(booktemp.ISBN, "end")) { break; } n = Insert(dictList, n, &booktemp); } num = n - num; // * 记录现有文件和原来文件的差值 printf("成功从文件录入了 %d 本图书!(共 %d 本)n", num, n); // * 录入成功的提示信息 fclose(fp); system("pause"); return n; } // * 将记录写回文件 void WriteText(Bookinfo dictList[], int n, char* filename) { system("cls"); Beauty(); // * 文件写入会破坏原文件,所以在这里提示一下 if (!Ask()) { return; } FILE* fp; // * 打开并判断文件打开是否失败 if ((fp = fopen(filename, "w")) == NULL) { printf("文件打开失败!!!n"); return; } int i;// * 循环变量 for (i = 0; i < n; i++) { // * 判断文件写入是否成功 if (!FILEOutputBookInformation(dictList[i], fp)) { printf("文件写入失败!n"); system("pause"); return; } } Beauty(); printf(" 文件写入成功!nn"); BigSpace(); printf(" 写入了 %d 个文件n", n); system("pause"); fclose(fp); } // * 将表反序存放 void Reverse(Bookinfo dictList[]) { system("cls"); Bookinfo temp[NUM]; // * 同样定义一个数组,和原数组长度相同,用来存放原来数组信息的信息 int i; int n; // * 记录数组长度 // * 先用temp储存数组原来信息 for (i = 0; dictList[i].edition != 0; i++) { temp[i] = dictList[i]; } n = i; // * 反序存放 for (i = 0; i < n; i++) { dictList[i] = temp[n - i - 1]; } Beauty(); printf("反序存放成功!n"); system("pause"); } // * 删除雷同记录 int DeleteSame(Bookinfo dictList[], int n) { int temp = n; // * 记录原来的数目 int i, j; for (i = 0; i < n; i++) { for (j = 0; j < n; j++) { if (!strcmp(dictList[i].ISBN, dictList[j].ISBN) && i != j) { n = DeleteByISBN(dictList, n, dictList[j].ISBN); j--;// * 删除之后再判断一次这个点,防止如果两个要删的连在一起后面那个删不了 } } } Beauty(); printf("成功删除了 %d 本书!n", temp - n); system("pause"); return n; } // * 退出系统 int Quit(Bookinfo* dicList[], int n) { system("cls"); int judge;// * 用来判断的变量 Enter(); Space(); printf(" 是否退出程序?(y/n)"); while (1) { judge = getch(); if (judge == 'y' || judge == 'Y') { system("cls"); Beauty(); printf("GOOD BYE!nnnnn"); exit(0); } else if (judge == 'n' || judge == 'N') return 0; } exit(0); } //**************************************************************************************************************** // * 这里面写的是一些辅助的函数 // * 用来输入一本书的信息 Bookinfo InputBookInformation() { Beauty(); Bookinfo book; printf("请输入书号:"); gets(book.ISBN); BigSpace(); printf("请输入书名:"); gets(book.book); BigSpace(); printf("请输入作者:"); gets(book.author); BigSpace(); printf("请输入版本号:"); scanf("%d", &book.edition); getchar();// * 消化掉scanf带来的换行符 BigSpace(); printf("请输入出版社名:"); gets(book.press); BigSpace(); printf("请输入出版年:"); scanf("%d", &book.year); getchar();// * 消化掉scanf带来的换行符 return book; } // * 输出书籍的信息 void OutputBookInformation(Bookinfo book) { printf("%-15s%-15s%-15s%-10d%-30s%-10dn", book.ISBN, book.book, book.author, book.edition, book.press, book.year); return; } // * 从文件中读取一本书的信息 Bookinfo freadBookInformation(FILE* fp) { Bookinfo book; fscanf(fp, "%s", book.ISBN); // * 判断是否到达结尾 // * 因为到结尾的时候还有一行是空的,所以book里面会多读一个数据 // * 这里让book.ISBN消化了那个数据后再判定终止条件 if (feof(fp)) { strcpy(book.ISBN, "end"); return book; } fscanf(fp, "%s", book.book); fscanf(fp, "%s", book.author); fscanf(fp, "%d", &book.edition); fscanf(fp, "%s", book.press); fscanf(fp, "%d", &book.year); return book; } // * 输出书籍的信息到文件中 int FILEOutputBookInformation(Bookinfo book, FILE* fp) { int judge = fprintf(fp, "%-15s%-15s%-15s%-10d%-30s%-10dn", book.ISBN, book.book, book.author, book.edition, book.press, book.year); // * 判断是否输入成功 if (judge <= 0) { return 0; } return 1; } // * 根据书号删除相应的图书 int DeleteByISBN(Bookinfo dictList[], int n, char ISBN[]) { int i, j; for (i = 0; i < n; i++) { if (!strcmp(ISBN, dictList[i].ISBN)) { // * 之后的都前移一位 for (j = i; j < n - 1; j++) { dictList[j] = dictList[j + 1]; } n--; } } return n; } // * 根据书号和姓名删除相应的图书 int DeleteByISBNAndName(Bookinfo dictList[], int n, char ISBN[], char bookname[]) { int i, j; for (i = 0; i < n; i++) { if (!strcmp(ISBN, dictList[i].ISBN) && !strcmp(bookname, dictList[i].book)) { for (j = i; j < n - 1; j++) { dictList[j] = dictList[j + 1]; } i--; n--; } } return n; } // * 管理员登录 void Signin() { int i;// * 用来循环输入密码便于隐藏 int j = 0; char account[40]; while (1) { char password[50] = { ' ' };// * 初始化密码数组 Star(); Space(); Space(); printf("The Book Management Systemnn"); Star(); Space(); Space(); printf("account:"); scanf("%s", account); getchar();// * 消化掉换行符 Space(); Space(); printf("npassword:"); system("cls"); i = 0;// * 隐藏密码 while (1) { Star(); BigSpace(); printf("The Book Management Systemnn"); Star(); BigSpace(); printf("account:"); printf("%snn", account); BigSpace(); printf("password:"); for (j = 0; j < i; j++) printf("*"); password[i] = getch(); if (password[i] == 'r') break; // * 输入backspace的时侯删除字符串中的字符 if (password[i] == 'b') { password[i] = ' '; i--; // * 防止减多了 if (i != -1) i--; } system("cls"); i++; if (i > 20) { break; } } // * 比较是否一致 if (!strcmp(account, "admin") && !strcmp(password, "123456r")) { system("cls"); return; } printf("nnn"); Space(); printf("the account number or password entered is incorrectnnnn"); BigSpace(); system("pause"); system("cls"); } } // * 输出书籍的表头信息 void OutputBookHead() { Enter(); printf("%-15s%-15s%-15s%-10s%-30s%-10snn", "书号", "书名", "作者", "版本号", "出版社名", "出版年份"); } // * 判断是否有相同的书名, 是返回1,否返回0 int JudgeSameName(Bookinfo dictList[], int n, char bookname[]) { int i; for (i = 0; i < n; i++) { if (!strcmp(dictList[i].book, bookname)) { return 1; } } return 0; } // * 在写入文件之前进行询问 int Ask() { int judge;// * 用来判断的变量 Enter(); Space(); printf(" 该操作可能会破坏原有文件,是否继续写入?(y/n)"); while (1) { judge = getch(); if (judge == 'y' || judge == 'Y') { system("cls"); return 1; } else if(judge == 'n' || judge == 'N') return 0; } }
程序优点
1)美化了界面,定义了四个函数用来控制控制台显示的位置,使得每一步的提示信息都尽可能的居中
2)在进入操作的程序之前设置了一个登录的界面,只有输入正确的账号密码才能进行使用
(账号:admin,密码:123456)同时在输入密码时也将输入的字符用*替代,保证了输入的安全性与系统的安全性
3)在Display功能中,每十个数据进行输出时,巧妙的使它拥有了翻页功能 而不只是把文件显示十个停顿,导致如果按的速度过快只能重新进行一次,提高了使用程序的效率
4)程序清晰易懂,可读性高,程序所有的细节都写好了注释,且每个函数相对独立,不会对其他函数造成很大影响和,定义了9个辅助操作的函数,将读取所有数据的操作变成一个一个数据进行读取,将读取一个文件,写入一个文件等操作作为一个单独的函数写出来,提升了程序的可读性
5)全程序使用了很多的提示语句,在关键的位置都有语句提示,保证了使用的便捷
如:
6)程序在翻页、选择是否继续等操作时,都设定了按其他按键不反应的功能,可以有效地防止误触



