#include<stdio.h> //其中包含feof();
#include<string.h> //包含strcmp(); strchr(); strcpy();
#include<stdlib.h> //包含malloc(); free(); system(); exit();
#include<io.h> //为access()提供定义
#define BROWSE printf("书名 \t 作者 \t 归属分类 \t 出版社 \t 出版时间 \t ISBN码 \t 价格\n");_ui(); printf("%s \t %s \t %s", current->book_name, current->author, current->classification); printf("%s \t %s \t %s \t %0.2lf\n", current->press, current->publish_time, current->ISBN, current->price);_ui();
typedef struct book {
char ISBN[15];
char book_name[30];
char author[20];
char classification[20];
char press[30];
char publish_time[5];
double price;
struct book *next;
}library; // ISBN码,书名,作者名,分类名,出版单位,出版时间,价格等
void _ui() //菜单分割线
{
printf("\t\t");
for (int i = 0; i<49; i++)
printf("-");
printf("\n");
}
void main_menu() //主菜单
{
system("color F0"); //背景色及字体
_ui(); printf("\t\t|\t\t图书信息管理系统\t\t|\n"); _ui();
printf("\t\t|\t\t1:录入\t\t\t\t|\n"); _ui();
printf("\t\t|\t\t2:浏览\t\t\t\t|\n"); _ui();
printf("\t\t|\t\t3:查询\t\t\t\t|\n"); _ui();
printf("\t\t|\t\t4:排序\t\t\t\t|\n"); _ui();
printf("\t\t|\t\t5:删除与修改\t\t\t|\n"); _ui();
printf("\t\t|\t\t6:保存到文件\t\t\t|\n"); _ui();
printf("\t\t|\t\t7:退出\t\t\t\t|\n"); _ui();
printf("\t\t输入你的选项(1~7):");
}
library * _search(const library * prev,char *keywords,int choice) //查找节点
{
//choice的值,1对应书名查找,2对应作者查找,3对应ISBN码查找
library * temp = prev->next;
switch (choice) {
case 1: for (; temp->next != NULL&&temp->book_name != keywords; temp = temp->next); break;
case 2: for (; temp->next != NULL&&temp->author != keywords; temp = temp->next); break;
case 3: for (; temp->next != NULL&&temp->ISBN != keywords; temp = temp->next); break;
}
return temp;
}
char * ch_gets(char * ch, int n) //专用于输入是否有效以及清除多余的‘\n’,参考C primer plus代码中代码,只为字符输入,但有效减少输入流里的'\n'
{
char * return_val;
char * find;
return_val = fgets(ch, n, stdin); //fgets()函数会录入'\n',相对应的fputs不会添加'\n',ch对应指针,n对应需要的字符串大小(用于限定大小还是挺好的),stdin标准输入流
if (return_val)
{
find = strchr(ch, '\n'); //检查ch的指针指向的内容,这样是查找ch里的'\n',匹配就把地址赋给find;
if (find) //判断find是否为NULL
*find = '\0'; //不是NULL的就把原来的'\n'变成'\0',
else //当为空NULL时,运行下面清除可能存在的换行符
while (getchar() != '\n')
continue;
}
return return_val;
}
library * _entry(library * book_head) //(1)录入,之前文件没有数据的则进行新数据录入,以新链表进行存储,后面返回头head地址
{
char input[30];
library *head=book_head,*prev,*current; //保存book_head传过来的地址
for (; book_head->next == NULL; book_head = book_head->next) //调整book_head到最后一个节点的next,使后面的录入连接上
continue;
_ui(); printf(" \t\t|\t\t图书信息管理系统:录入\t\t|\n"); _ui();
if ((book_head->next = prev = current = (struct book *)malloc(sizeof(library))) == NULL) //开始分配内存,并相同指向同个位置
{puts("无法分配内存..."); system("pause"); return NULL;}
printf("添加一本新书的名字(回车则返回菜单):\n");
while (ch_gets(input, 30) != NULL&& input[0] != '\0')
{
if ((current = (struct book *)malloc(sizeof(library))) == NULL)
{puts("无法分配内存..."); system("pause"); return NULL;}
strcpy(current->book_name, input);
puts("输入作者名:");
ch_gets(current->author, 20);
puts("输入归属分类:");
ch_gets(current->classification, 20);
puts("输入出版社:");
ch_gets(current->press, 30);
puts("输入出版时间:");
ch_gets(current->publish_time, 5);
puts("输入ISBN码:");
ch_gets(current->ISBN, 15);
puts("输入价格:");
scanf("%lf", ¤t->price); while (getchar() != '\n') continue;
puts("继续输入新书名(直接回车返回主菜单):");
prev->next=current; //第一次current指向head,后面的就是上次的perv->next
prev = current; //把当前的current指向perv,用于下次的指向;
}
prev->next = NULL;
return head;
}
void _browse(const library *head) //(2)浏览,只需要链表首地址
{
const library *current= head;
char choice[2] = {'u'};
_ui(); printf("\t\t|\t图书信息管理系统:浏览(回车返回主菜单)\t|\n"); _ui();
if (head == NULL)
{
puts("没有数据");
system("pause");
return;
}
else
if (head->next == NULL) //判断是否为一个节点
{
if (!head->book_name) //判断有无数据 系统是否会有垃圾值??
{
BROWSE;
}
}
else
{
printf("书名 \t 作者 \t 归属分类 \t 出版社 \t 出版时间 \t ISBN码 \t 价格\n"); _ui();
current = head;
for (current = current->next; current != NULL; current = current->next)
{
printf("%s \t %s \t %s", current->book_name, current->author, current->classification);
printf("%s \t %s \t %s \t %0.2lf\n", current->press, current->publish_time, current->ISBN, current->price); _ui();
current = current->next;
}
}
system("pause");
}
void _inquire(const library *head) //(3)查询
{
char choice, select[30];
const library *current=head;
_ui(); printf("\t\t|\t\t图书信息管理系统:查询\t\t|\n"); _ui();//书名或作者名查询
printf("Y:默认书名查询,N:作者名查询,回车返回菜单(Y/N):");
while (ch_gets(&choice, 1) != NULL&& choice != '\0')
{
if (choice == 'Y' || choice == 'y')
{
puts("输入书名:");
ch_gets(select, 30);
current = _search(current,select,1);
if (current != NULL) //能找找到匹配项时
{BROWSE; system("pause");}
else
{puts("没有结果"); system("pause");}
}
else if (choice == 'N' || choice == 'n')
{
puts("输入作者名:");
ch_gets(select, 30);
current = _search(current,select,2);
if (current!=NULL)
{BROWSE; system("pause");}
else
{puts("没有结果"); system("pause");}
}
else
puts("你输入有误,请再输入:");
}
}
library * _sorting(library *head) //(4)排序
{
char choice;
library *current= head->next, *prev= head, *temp=NULL;
if ((temp = (struct book *)malloc(sizeof(library))) == NULL)
{puts("无法分配内存..."); system("pause"); return NULL;}
_ui(); printf("\t\t|\t\t图书信息管理系统:排序\t\t|\n"); _ui();//书名或价格排序
printf("Y:默认书名排列,N:价格排列,回车返回菜单(Y/N):");
while (ch_gets(&choice, 1) != NULL && choice != '\0')
{
if (choice == 'Y' || choice == 'y')//书名排序
{
for (; strcmp(prev->book_name, current->book_name)>0 || current != NULL; current = current->next)
{
temp = prev;
prev = current->next;
current->next = temp;
}
_browse(head);
}
else if (choice == 'N' || choice == 'n')//价格排序
{
printf("Y:从大到小排列,N:从小到大排列,回车返回菜单(Y/N):");
while (ch_gets(&choice, 1) != NULL&& choice != '\0')
{
if (choice == 'Y' || choice == 'y')//从大到小
{
for (; prev->price > current->price || current != NULL; current = current->next)
{
temp = prev;
prev = current->next;
current->next = temp;
}
_browse(head);
}
else if (choice == 'N' || choice == 'n') //从小到大
{
for (; prev->price > current->price|| current != NULL; current = current->next)
{
temp = current->next;
current->next = prev;
prev = temp;
free(temp);
}
_browse(head);
}
}
}
else
{
puts("你输入有误,请再输入:");
scanf("%c", &choice);
}
}
free(temp);
return head;
}
library * menu_modify(library *current) //用于下面”_delete_modify“函数的删除与修改的选项菜单以及其功能实现
{
char choice;
_ui(); printf("\t\t|\t\t图书信息管理系统:删除与修改(操作菜单)\t|\n"); _ui();
printf("\t\t|(1)书名\t\t(2)作者\t\t|\n"); _ui();
printf("\t\t|(3)归属分类 \t\t(4)出版社\t\t|\n"); _ui();
printf("\t\t|(5)出版时间 \t\t(6)ISBN码\t\t|\n"); _ui();
printf("\t\t|(7)价格 \t\t(8)删除此条记录\t|\n"); _ui();
puts("选择的操作数据的选项(回车取消操作,返回上一层):\n");
while (ch_gets(&choice, 1) != NULL&& choice != '\0')
{
switch (choice) {
case 1:
puts("输入你修改的书名:");
ch_gets(current->book_name, 20); break;
case 2:
puts("输入你修改的作者:");
ch_gets(current->author, 20); break;
case 3:
puts("输入你修改的归属分类:");
ch_gets(current->classification, 20); break;
case 4:
puts("输入你修改的出版社:");
ch_gets(current->press, 30); break;
case 5:
puts("输入你修改的出版时间:");
ch_gets(current->publish_time, 5); break;
case 6:
puts("输入你修改的ISBN码:");
ch_gets(current->ISBN, 5); break;
case 7:
puts("输入你修改的价格:");
scanf("%lf", ¤t->price); while (getchar() != '\n') continue; break;
case 8:current = current->next; return NULL; break;
}
}
return current;
}
library * _delete_modify(library *head) //(5)删除与修改
{
library *current=head;
char temp_input[30];
_ui(); printf("\t\t|\t\t图书信息管理系统:删除与修改\t|\n"); _ui();
printf("\t\t默认匹配修改,回车返回主菜单\n");
printf("输入书名或ISBN码:");
while (ch_gets(temp_input, 30) != NULL&& temp_input[0] != '\0')
{
current = _search(current,temp_input,1); //使用书名查找
current = _search(current, temp_input,3); //使用ISBN码查找
if(current!=NULL)
{
BROWSE;
_ui(); current=menu_modify(current);
if(current!=NULL) //删除时不显示修改后结果
BROWSE;
system("pause");
}
else
puts("你输入有误,请再输入:");
}
return head;
}
library * open_file() //打开文件,参考修改
{
FILE *fbook;
library *prev,*current,*head;
if (access("book_mg.txt", 0)==0) //判断文件是否存在,存在返回零
fbook = fopen("book_mg.dat", "rb");
else
{
fbook = fopen("book_mg.dat", "a");
fclose(fbook); puts("创建book_mg.dat成功");
fbook = fopen("book_mg.dat", "rb");
}
if ((head = current = prev = (library *)malloc(sizeof(library))) == NULL) //使三个指针的地址相同,首地址head确定
{puts("无法分配内存..."); system("pause"); return NULL;}
else
head->price=0; //类似的再次确定内存可访问;随意让首地址确定下来,觉得返回NULL不好。并将其中的成员赋值,原因是因为如果文件没内容时,head指针跟空了似的,好像把初始出来的内存垃圾值给其成员
if (fbook == '\0') //判断文件是否有内容
{
fclose(fbook);
}
else
{
while (!feof(fbook)) //返回非零值代表已到达文件尾.说明零值时文件还有数据可输入
{
if ((current = (library *)malloc(sizeof(library))) == NULL)
{puts("无法分配内存..."); system("pause"); return NULL;}
fread(current, sizeof(library), 1, fbook); //二进制读取,因为书的价格是double类型,为不丢失精度
//fscanf(fbook,"%s %s %s %s %s %s %lf", current->book_name, current->author, current->classification, current->press, current->publish_time, current->ISBN, ¤t->price); //文本模式读取
prev->next = current; //因为prev在上面确定首地址时已经把prev地址指向head,第一次运行说明第一个current指向head->head
prev = current; //为下次输入进行保留本次地址
}
prev->next = NULL;//最后完成输入的节点需要最后的指针指空,不然会变野指针
fclose(fbook);
}
return head;
}
void _save(library *head) //(7)存储
{
FILE *fbook;
library *prev = head;
_ui(); printf("\t\t|\t图书信息管理系统:保存到文件\t\t|\n"); _ui();
if ((fbook = fopen("book_mg.txt", "wb")) == NULL)
{puts("文件不能打开...");return;}
if (head->next != NULL) //说明还有节点可以输入保存到文件里
{
for (prev=prev->next; prev != NULL; prev = prev->next) //最开始prev指向了head,而由于打开文件时初始化的head是没有用的,所以prev=prev->next;舍弃第一个节点从第二个开始存
fwrite(prev, sizeof(library), 1, fbook); //二进制保存
//fprintf(fbook, "%s %s %s %s %s %s %lf", prev->book_name, prev->author, prev->classification, prev->press, prev->publish_time, prev->ISBN, prev->price); //保存
}
fclose(fbook);
puts("保存完毕..."); system("pause");
}
int main(void) //
{
int choice=-1;
library * book_head=open_file(); //初始化,并给首地址
while (1)
{
system("cls");
main_menu();
if (scanf("%d", &choice)) //选项选择,带纠错
{
if ((choice > 1 && 7 < choice))
printf("你的输入有误,请再输入:");
}
system("cls");
while (getchar() != '\n') //防止'\n'
continue;
switch (choice) {
case 1:book_head=_entry(book_head);break; //(1)录入
case 2:_browse(book_head); break; //(2)浏览
case 3:_inquire(book_head); break; //(3)查询
case 4:book_head=_sorting(book_head); break; //(4)排序
case 5:book_head=_delete_modify(book_head); break; //(5)删除与修改
case 6:_save(book_head); break; //(6)存储
case 7:free(book_head);
exit(0);
}
}
return 0;
}