题目要求
原题目太长,我提取和整合了一下核心要求。发现大部分之前都训练过,将之前写过的代码改一下就可以。
1、题目描述
设计一个图书登记管理程序,该程序具有以下功能:
链表的增删改查:
(1)录入某本图书的信息(图书信息包括的内容:ISBN号、书名、作者、出版社、出版日期、价格)
(2)给定图书ISBN编号,显示、修改、删除该图书信息
(6)给定出版社名称,查找并显示该出版社的所有图书的信息;
(7)统计功能:提供一些统计各类信息的功能。
匹配字符串:
(5)给定某个字符串,查找并显示所有书名中包括该字符串的图书的信息;
2、题目要求
(3)程序的各项功能在程序运行时,以菜单方式选择并执行;
(5)所有的信息存储在一个文件或多个中,并实现文件读写操作。
(6)程序中用链表存放图书信息并实现增删减功能。(必须)
一些废话
前不久写了一个c语言数据库操作(<–点他)的小项目,相比之下,这个项目的层次没有这么多,只有两层,有了上一次的经验这个项目写起来很快,一天不到就大体写好了,过程非常愉快。上一个项目的实现对输入输出缓冲区和文件的要求比较高,而这个要求你必须用链表实现,主要难点(对于我这种指针渣渣来说)都在指针操作(一个bug改了两天)。从上面的归纳就不难看出除了匹配字符串其他都是链表操作。值得一提的是,前不久刚好看过一篇讲使用哈希表来提高匹配速度的算法(RK),在这里我用c写了一下(发现别人大多都是cpp)。当然如果你有更高的追求可以使用kpm算法之类的,据我所知匹配字符串的方法有很多,比这个快的也有很多比如这个,但是谁让我才疏学浅呢,高级的算法还是等以后吧。。。
这次编程顺风顺水除了一个bug***segmentaion fault***(眼熟到能背下来了,毕竟这两天一调试就冒出这个)。网上看了无数文章也不知道我哪里有野指针,知道请教了别人之后有所启发,然后发现其实归根结底问题只有一个,我传参的时候把func(head)写成了func(*head)。知道真相的我眼泪掉下来。气死了。
要点的实现
重要结构
上一个项目是这样的实现的
switch(func){
case 1:while(create_database());break;//创建数据库
case 2:while(delete_database());break;//删除数据库
case 3:while(use_database());break;//打开数据库
case 4:exit(0);break;
}
他们的返回值类型是int(也可以是bool但是要引入stdbool.h),由于要求用指针实现所以我就理所当然的认为函数的返回值只能值指针类型了,所以我在结构体里面加了一个flag的字段,只有head指针使用(现在想想反正头指针不装数据其实可以用随便一个数据来代替flag,不过我懒得改了)。然而如果把head声明为结构体而不是指针的话其实就可以实现返回值仍然为bool或int,不过因为我的链表操作是三脚猫功夫所以当时没想到,直到请教别人的时候(其实是帮我debug【狗头)我才知道的。
switch(choice){多次调用直至用户选择回到菜单
case 1:while(head->flag){
head = add_book(head);
}break;//增
case 2:while(head->flag){
head = delete_book(head);
}break;//删
case 3:while(head->flag){
head = update_book(head);
}break;//改
case 4:while(head->flag){
head = select_book(head);
}break;//查
case 5:while(head->flag){
head = find_books(head);
}break;//模糊查询
case 6:while(head->flag){
head = select_publish(head);
}break;//出版社
case 7:while(head->flag){
head = statistic(head);
}break;//统计
case 8:writer(head);exit(0);
default:printf("异常输入!");Sleep(1000);break;
删除一个节点
这里的指针和其他不太一样,而除了指针操作部分其他函数和他都是一个模子出来的。
Book* delete_book(Book *head){//删
system("cls");
int flag=0,ISBN,choice;//标记是否找到
printf("删除图书信息\n");
printf("=====================\n");
printf("请输入ISBN号:");
scanf("%d",&ISBN);
Book *now;
now=(Book*)malloc(sizeof(Book));
now=head;//配合下面,使得第一个指针可以被读取
while(now->next){
if(now->next->ISBN==ISBN){//匹配下一个是否符合
flag=1;
break;
}
now=now->next;
}
if(flag==1){
now->next=now->next->next;//删掉now->next那个
printf("删除成功!");
Sleep(1000);
}
else{
printf("查无此书!");
Sleep(1000);
}
system("cls");
printf("现在你有两个选择\n");
printf("=================\n");
printf(" 1.继续删除\n 2.返回菜单\n");
printf("=================\n");
printf("请输入你的选择:");
scanf("%d",&choice);
switch(choice){
case 1:head->flag=true;break;
case 2:head->flag=false;break;
default:printf("异常输入!");
}
Sleep(1000);
return head;
}
RK算法匹配字符串
bool match_str(char *mstr,char *pstr){
int i,j,k,m,p,flag=0,hash_p=0,hash_m=0;
m=strlen(mstr);//待匹配字符串长度(长的那个)
p=strlen(pstr);//模板字符串长度(短的那个)
for(i=0;i<p;i++){//计算模板哈希值
hash_p += pstr[i]-'a'+1;
}
for(i=0;i<m-p+1;i++){//开始匹配
if(i==0){//判断是否为第一次计算带匹配字符串
for(k=i;k<p+i;k++){//计算待匹配字符串的第一个哈希值
hash_m += mstr[k]-'a'+1;
}
}
else{//从第二个起,哈希值计算变为:上一个哈希值减去首字符在加上尾字符
hash_m += (mstr[i-1+p] - mstr[i-1]);
}
if(hash_m==hash_p){//判断哈希值是否相等
for(j=0;j<p;j++){//逐个字符比对
if(pstr[j]!=mstr[i+j]){
break;//发现不相等就跳出循环继续寻找
}
}
if(j==p){//如果j==p的话说明上面的循环没有被跳出,两个字符串相等
flag=1;
break;//因为是要找最早的一个所以找到即退出
}
}
}
if(flag==1){
return true;//找到
}
else{
return false;//找不到
}
}
全部代码
#include<stdio.h>
#include<stdlib.h>//system,exit
#include<string.h>
#include<windows.h>
#include<stdbool.h>//c默认没有bool类型,要引入这个
#define SIZE 10
#define NUM 40
FILE *fp;
typedef struct Book{
int ISBN;//isbn号是10位数字,理论上可以达到99亿多而int只有42亿
char bookName[SIZE];//书名
char publisher[SIZE];//出版社
char author[SIZE];//作者
int publishDate;//出版日期,如:20190306
double price;//价格
struct Book *next;
bool flag;//只有head会使用这个字段,来判断是否循环
}Book;
//第一层
Book* menu();//功能菜单
//淡黄的长裙,蓬松的头发......
Book* reader();//数据读取器
Book* writer();//数据写入器
//第二层
Book* add_book();//增
Book* delete_book();//删
Book* update_book();//改
Book* select_book();//查
Book* find_books();//模糊查找
Book* select_publish();//出版社
Book* statistic();//统计
bool match_str();//匹配字符串
int main(){
Book *head;
head=(Book*)malloc(sizeof(Book));
head->flag=true;//进入循环
printf("正在打开数据库......\n");
Sleep(1000);//延时一秒,可以装一下b
head = reader(head);//从数据库读取数据
while(head->flag){//多次调用直至退关闭程序
head = menu(head);
}
return 0;
}
//第一层
Book* menu(Book *head){
system("cls");//清屏,实现跳转效果
system("title 图书登记管理系统");//设置cmd窗口标题
system("mode con cols=30 lines=25");//窗口宽度高度
printf("系统菜单\n");
printf("=====================\n");
printf(" 功能选项:\n");
printf("1.增加图书信息\n");
printf("2.删除图书信息\n");
printf("3.修改图书信息\n");
printf("4.查询图书信息\n");
printf("5.模糊查询图书\n");
printf("6.查询出版社信息\n");
printf("7.查看统计数据\n");
printf("8.退出程序\n");
printf("=====================\n");
printf("请输入你的选择:");
int choice;
scanf("%d",&choice);
switch(choice){多次调用直至用户选择回到菜单
case 1:while(head->flag){
head = add_book(head);
}break;//增
case 2:while(head->flag){
head = delete_book(head);
}break;//删
case 3:while(head->flag){
head = update_book(head);
}break;//改
case 4:while(head->flag){
head = select_book(head);
}break;//查
case 5:while(head->flag){
head = find_books(head);
}break;//模糊查询
case 6:while(head->flag){
head = select_publish(head);
}break;//出版社
case 7:while(head->flag){
head = statistic(head);
}break;//统计
case 8:writer(head);exit(0);
default:printf("异常输入!");Sleep(1000);break;
}
head->flag=true;
return head;
}
Book* reader(Book *head){//数据读取
int n=0;
fp=fopen("__DataBase.txt","r");
Book *now=head;
Book *temp;
temp=(Book*)malloc(sizeof(Book));
while(fscanf(fp,"%d,%d,%lf",&temp->ISBN,&temp->publishDate,&temp->price)!=EOF){
fscanf(fp,"%s",temp->bookName);
fscanf(fp,"%s",temp->publisher);
fscanf(fp,"%s",temp->author);
//尾插法
now->next=temp;
temp->next=NULL;
now=now->next;
temp=(Book*)malloc(sizeof(Book));
n++;
}
printf("成功读取%d条数据",n);
free(temp);
free(now);
fclose(fp);
Sleep(1000);
return head;
}
Book* writer(Book *head){//数据写入器
int n=0;
fp=fopen("__DataBase.txt","w");
Book *now;
now=(Book*)malloc(sizeof(Book));
now=head->next;
while(now){
fprintf(fp,"%d,%d,%lf\n",now->ISBN,now->publishDate,now->price);
fprintf(fp,"%s\n",now->bookName);
fprintf(fp,"%s\n",now->publisher);
fprintf(fp,"%s\n",now->author);
now=now->next;
n++;
}
printf("成功存储%d条数据",n);
fclose(fp);
Sleep(1000);
return head;
}
//第二层
Book* add_book(Book *head){//增
int choice;
Book *temp,*now;
temp=(Book*)malloc(sizeof(Book));
now=(Book*)malloc(sizeof(Book));
system("cls");
printf("增加图书信息\n");
printf("=====================\n");
printf("请输入ISBN号:");
scanf("%d",&temp->ISBN);
printf("请输入书名:");
scanf("%s",temp->bookName);
printf("请输入作者:");
scanf("%s",temp->author);
printf("请输入出版社:");
scanf("%s",temp->publisher);
printf("请输入出版日期(如:20200401):");
scanf("%d",&temp->publishDate);
printf("请输入价格:");
scanf("%lf",&temp->price);
//头插法
now=head->next;
head->next=temp;
temp->next=now;
system("cls");//使用功能后,判断是否继续使用
printf("现在你有两个选择\n");
printf("=================\n");
printf(" 1.继续插入\n 2.返回菜单\n");
printf("=================\n");
printf("请输入你的选择:");
scanf("%d",&choice);
switch(choice){
case 1:head->flag=true;break;//循环插入
case 2:head->flag=false;break;//返回菜单
default:printf("异常输入!");
}
Sleep(1000);
return head;
}
Book* delete_book(Book *head){//删
system("cls");
int flag=0,ISBN,choice;//标记是否找到
printf("删除图书信息\n");
printf("=====================\n");
printf("请输入ISBN号:");
scanf("%d",&ISBN);
Book *now;
now=(Book*)malloc(sizeof(Book));
now=head;//配合下面,使得第一个指针可以被读取
while(now->next){
if(now->next->ISBN==ISBN){//匹配下一个是否符合
flag=1;
break;
}
now=now->next;
}
if(flag==1){
now->next=now->next->next;//删掉now->next那个
printf("删除成功!");
Sleep(1000);
}
else{
printf("查无此书!");
Sleep(1000);
}
system("cls");
printf("现在你有两个选择\n");
printf("=================\n");
printf(" 1.继续删除\n 2.返回菜单\n");
printf("=================\n");
printf("请输入你的选择:");
scanf("%d",&choice);
switch(choice){
case 1:head->flag=true;break;
case 2:head->flag=false;break;
default:printf("异常输入!");
}
Sleep(1000);
return head;
}
Book* update_book(Book *head){//改
system("cls");
int choice=0,ISBN;
printf("修改图书信息\n");
printf("=====================\n");
printf("请输入ISBN号:");
scanf("%d",&ISBN);
Book *now;
now=(Book*)malloc(sizeof(Book));
now=head->next;
while(now){
if(now->ISBN==ISBN){
choice=1;
while(choice){
printf("你要修改:");
printf("1.书名\n");
printf("2.作者\n");
printf("3.出版社\n");
printf("4.出版日期\n");
printf("5.价格\n");
printf("6.修改完此书了\n");
scanf("%d",&choice);
switch(choice){
case 1:printf("请输入新书名:");scanf("%s",now->bookName);break;
case 2:printf("请输入作者:");scanf("%s",now->author);break;
case 3:printf("请输入出版社:");scanf("%s",now->publisher);break;
case 4:printf("请输入出版日期(如:20200401):");scanf("%d",&now->publishDate);break;
case 5:printf("请输入价格:");scanf("%lf",&now->price);break;
case 6:choice=0;break;
}
}
printf("修改成功!");
Sleep(1000);
break;
}
now=now->next;
}
if(choice==1){
printf("查无此书...");
Sleep(1000);
}
//常规操作
system("cls");
printf("现在你有两个选择\n");
printf("=================\n");
printf(" 1.修改其他书\n 2.返回菜单\n");
printf("=================\n");
printf("请输入你的选择:");
scanf("%d",&choice);
switch(choice){
case 1:head->flag=true;break;
case 2:head->flag=false;break;
default:printf("异常输入!");
}
Sleep(1000);
return head;
}
Book* select_book(Book *head){//查
system("cls");
int choice,ISBN;
printf("查询图书信息\n");
printf("=====================\n");
printf("请输入ISBN号:");
scanf("%d",&ISBN);
Book *now;
now=(Book*)malloc(sizeof(Book));
now=head->next;
while(now){
if(now->ISBN==ISBN){
printf("书名:%s\n",now->bookName);
printf("作者:%s\n",now->author);
printf("出版社:%s\n",now->publisher);
printf("出版日期:%d\n",now->publishDate);
printf("价格:%lf\n",now->price);
system("pause");
//下面的也可以
//fllush(stdin);
//scanf("%*c");
break;
}
now=now->next;
}
system("cls");
printf("现在你有两个选择\n");
printf("=================\n");
printf(" 1.继续删除\n 2.返回菜单\n");
printf("=================\n");
printf("请输入你的选择:");
scanf("%d",&choice);
switch(choice){
case 1:head->flag=true;break;
case 2:head->flag=false;break;
default:printf("异常输入!");
}
Sleep(1000);
return head;
}
Book* find_books(Book *head){//模糊查找
system("cls");
int choice,num=0;//记录结果数
char str[SIZE];
printf("模糊查找图书\n");
printf("=====================\n");
printf("请输入字符串:");
scanf("%s",str);
Book *now;
now=(Book*)malloc(sizeof(Book));
now=head->next;
while(now){
if(match_str(now->bookName,str)){
printf("书名:%s\n",now->bookName);
printf("作者:%s\n",now->author);
printf("出版社:%s\n",now->publisher);
printf("出版日期:%d\n",now->publishDate);
printf("价格:%lf\n",now->price);
printf("------------------\n");
num++;
}
now=now->next;
}
printf("一共为你搜索到了%d条结果",num);
system("pause");
system("cls");
printf("现在你有两个选择\n");
printf("=================\n");
printf(" 1.继续查找\n 2.返回菜单\n");
printf("=================\n");
printf("请输入你的选择:");
scanf("%d",&choice);
switch(choice){
case 1:head->flag=true;break;
case 2:head->flag=false;break;
default:printf("异常输入!");
}
Sleep(1000);
return head;
}
Book* select_publish(Book *head){//出版社
system("cls");
int choice=0,n=0;//计数
char publisher[SIZE];
printf("查询出版社信息\n");
printf("=====================\n");
printf("请输入出版社名称:");
scanf("%s",publisher);
Book *now;
now=(Book*)malloc(sizeof(Book));
now=head->next;
while(now){
if(!strcmp(now->publisher,publisher)){
choice=1
printf("第%d本书:\n",n);
printf("书名:%s\n",now->bookName);
printf("作者:%s\n",now->author);
printf("出版社:%s\n",now->publisher);
printf("出版日期:%d\n",now->publishDate);
printf("价格:%lf\n",now->price);
printf("-----------------\n");
n++;
}
now=now->next;
}
if(choice==0){
printf("没有这个出版社的书...");
}
system("pause");
system("cls");
printf("现在你有两个选择\n");
printf("=================\n");
printf(" 1.查看其它\n 2.返回菜单\n");
printf("=================\n");
printf("请输入你的选择:");
scanf("%d",&choice);
switch(choice){
case 1:head->flag=true;break;
case 2:head->flag=false;break;
default:printf("异常输入!");
}
Sleep(1000);
return head;
}
Book* statistic(Book *head){//统计
system("cls");
printf("查看统计数据\n");
printf("=====================\n");
printf("数据如下:");
int i,choice,publisher_num=0,book_num=0,author_num=0;
double sum_price=0.0;
char author_list[NUM][15]={{0}};//初始化
char publisher_list[NUM][15]={{0}};
Book *now;
now=(Book*)malloc(sizeof(Book));
now=head->next;
while(now){
for(i=0;i<publisher_num;i++){
if(!strcmp(now->publisher,publisher_list[i])){//相同则跳出
break;
}
}
if(i==publisher_num){//若不相同则录入
strcpy(publisher_list[publisher_num++],now->publisher);
}
//同上
for(i=0;i<author_num;i++){
if(!strcmp(now->author,author_list[i])){
break;
}
}
if(i==author_num){
strcpy(author_list[author_num++],now->author);
}
sum_price += now->price;//统计价值
book_num++;//统计书的数量
now=now->next;
}
printf("系统中共记录%d本书:\n",book_num);
printf("系统中共记录%d个作者\n",author_num);
printf("系统中共记录%d个出版社\n",publisher_num);
printf("书籍的总价值达到%lf人民币\n",sum_price);
printf("-----------------\n");
system("pause");
system("cls");
printf("现在你有两个选择\n");
printf("=================\n");
printf(" 1.继续统计(???闲的你)\n 2.返回菜单\n");
printf("=================\n");
printf("请输入你的选择:");
scanf("%d",&choice);
switch(choice){
case 1:head->flag=true;break;
case 2:head->flag=false;break;
default:printf("异常输入!");
}
Sleep(1000);
return head;
}
//因为要频繁匹配所以用稍微快一点的算法
bool match_str(char *mstr,char *pstr){
int i,j,k,m,p,flag=0,hash_p=0,hash_m=0;
m=strlen(mstr);//待匹配字符串长度(长的那个)
p=strlen(pstr);//模板字符串长度(短的那个)
for(i=0;i<p;i++){//计算模板哈希值
hash_p += pstr[i]-'a'+1;
}
for(i=0;i<m-p+1;i++){//开始匹配
if(i==0){//判断是否为第一次计算带匹配字符串
for(k=i;k<p+i;k++){//计算待匹配字符串的第一个哈希值
hash_m += mstr[k]-'a'+1;
}
}
else{//从第二个起,哈希值计算变为:上一个哈希值减去首字符在加上尾字符
hash_m += (mstr[i-1+p] - mstr[i-1]);
}
if(hash_m==hash_p){//判断哈希值是否相等
for(j=0;j<p;j++){//逐个字符比对
if(pstr[j]!=mstr[i+j]){
break;//发现不相等就跳出循环继续寻找
}
}
if(j==p){//如果j==p的话说明上面的循环没有被跳出,两个字符串相等
flag=1;
break;//因为是要找最早的一个所以找到即退出
}
}
}
if(flag==1){
return true;//找到
}
else{
return false;//找不到
}
}