C语言实现简单电话簿(注释版)
C语言实现简单电话簿(注释版,可直接运行)
简单的小组实验记录,用到了文件读写、链表操作的知识,没有什么高端的东西,包含一个比较好看的煮菜单,字符画画得很开心。
文件有两个,一个.c文件(函数体以及主函数),一个.h文件(一大堆函数声明和一个结构体)。
运行时c文件、h文件以及数据文件放在同一路径下。
1.效果图以及函数调用关系
主要函数调用关系如图↓
2.c文件内容,包含注释(4组电话簿.c)
/*
分为两部分进行操作,一部分是链表(创建、显示、释放、修改),另一部分是是文件(读取和写入)
需要头文件,否则调用时出错
*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<malloc.h>
#include "Contacts.h"
int main()
{int mode;inf* head;ShowMenu(); //显示主菜单printf("\n 请选择需要的功能:\n");scanf("%d",&mode);getchar(); //保护switch while(1) //循环,用switch退出 {switch(mode){case 1: //创建电话簿:creat_chain & save_to_file & free_chainsystem("cls");printf("开始创建...\n"); head=creat_chain();save_to_file(head);free_chain(head);system("cls");ShowMenu();printf("输入其他服务项对应数字以继续\n");scanf("%d",&mode);getchar(); break;case 2: //查找联系人:load_chain & search & free_chainsystem("cls");head=load_chain(head);if(judge(head)==1) //在源头上检验空电话簿 {search(head);free_chain(head);system("cls");ShowMenu();printf("输入其他服务项对应数字以继续\n");scanf("%d",&mode);getchar(); }else //电话簿为空的情况,请回去重建 {printf("电话簿为空,请先创建或提供文件Contacts.dat\n"); system("pause");system("cls");ShowMenu();printf("输入其他服务项对应数字以继续\n");scanf("%d",&mode);getchar(); }break;case 3: //添加联系人:load_chain & add_chain & save_to_file & free_chain system("cls");head=load_chain(head);if(judge(head)==1){add_chain(head);save_to_file(head);free_chain(head);system("cls");ShowMenu();printf("输入其他服务项对应数字以继续\n");scanf("%d",&mode);getchar(); }else //电话簿为空的情况,请回去重建 {printf("电话簿为空,请先创建或提供文件Contacts.dat\n"); system("pause");system("cls");ShowMenu();printf("输入其他服务项对应数字以继续\n");scanf("%d",&mode);getchar(); }break;case 4: //修改联系人:load_chain & modify_chain & save_to_file & free_chainsystem("cls");head=load_chain(head);if(judge(head)==1){modify_node(head);save_to_file(head);free_chain(head);system("cls");ShowMenu();printf("输入其他服务项对应数字以继续\n");scanf("%d",&mode);getchar(); }else //电话簿为空的情况,请回去重建 {printf("电话簿为空,请先创建或提供文件Contacts.dat\n"); system("pause");system("cls");ShowMenu();printf("输入其他服务项对应数字以继续\n");scanf("%d",&mode);getchar(); }break;case 5: //删除联系人:load_chain & delete_node & save_to_file & free_chainsystem("cls");head=load_chain(head);if(judge(head)==1){delete_node(head);save_to_file(head);free_chain(head);system("cls");ShowMenu();printf("输入其他服务项对应数字以继续\n");scanf("%d",&mode);getchar(); }else //电话簿为空的情况,请回去重建 {printf("电话簿为空,请先创建或提供文件Contacts.dat\n"); system("pause");system("cls");ShowMenu();printf("输入其他服务项对应数字以继续\n");scanf("%d",&mode);getchar(); }break;case 6: //排序查看并存储:load_chain & sort/sort_by_name/sort_by_number &free_chainsystem("cls");head=load_chain(head);if(judge(head)==1){sort(head);head=load_chain(head);print_chain(head);system("pause");free_chain(head);system("cls");ShowMenu();printf("输入其他服务项对应数字以继续\n");scanf("%d",&mode);getchar(); }else //电话簿为空的情况,请回去重建 {printf("电话簿为空,请先创建或提供文件Contacts.dat\n"); system("pause");system("cls");ShowMenu();printf("输入其他服务项对应数字以继续\n");scanf("%d",&mode);getchar(); }break;case 7:system("cls");printf("\n\n退出\n\n");return 0;break;default:printf("非法输入,重新选择\n");scanf("%d",&mode);getchar(); break; } } return 0;
}
int judge(inf*head)
{if(head==NULL) return 0;else return 1;
}
void print_this_node(inf* node) //节点打印函数 √
{if(node==NULL) //空节点直接返回 {return;}printf("姓名 "); puts(node->name);printf("电话 ");puts(node->number);printf("地址 "); puts(node->address);printf("\n");return;
}
void ShowMenu() //展示lby菜馆的菜单 √
{printf("\n *欢迎来到联系人系统*\n");printf(" ┌------------------------$----------------------┐\n");//以下是主要调用关系↓ printf(" | 1--------创建新电话簿 | # |\n");//creat_chain & save_to_file & free_chainprintf(" | (覆盖 ) | ## |\n");printf(" | | # # |\n"); printf(" | 2--------查找联系人 | # # |\n");//load_chain & search/search_by_name/search_by_number/sreach_by_address &free_chainprintf(" | 3--------添加联系人 | # # |\n");//load_chain & add_chain & save_to_file & free_chainprintf(" | 4--------修改联系人 | # # |\n");//load_chain & modify_chain & save_to_file & free_chainprintf(" | 5--------删除联系人 | # # |\n");//load_chain & delete_chain & save_to_file & free_chainprintf(" | 6--------按序查看电话簿| ################# |\n");//load_chain & sort/sort_by_name/sort_by_number &free_chainprintf(" | (可选功能) | # |\n");printf(" | 7--------退出 | # 组 |\n");printf(" | v0.1 | # |\n");printf(" └------------------------$----------------------┘\n");return;
}
int length_of_chain(inf* head) //求出链表(以节点数表示的)长度 √
{int length= 0;inf* p=head;while(p != NULL) //看p走过了几个next {p = p->next;length++;}return length;
}
inf* creat_chain() //电话簿(链表)创建函数,返回链表头 √
{inf *p,*temp,*head;int head_flag; //正在创建链表头的标志,可修改它的值,便于循环创建 p=(inf*)malloc(sizeof(inf)); //申请一个结构体大小的空间,将其地址放到p中printf("输入姓名,输入 exit 结束添加\n");gets(p->name); //输入姓名 if(strcmp(p->name,"exit")==0) //退出条件 {return NULL;}else //正常创建第一个记录 {printf("请输入电话\n");gets(p->number);printf("请输入地址\n");gets(p->address);head_flag=1; //创建第一个记录后,标记链表头已经创建 }/*——————————————————— 以下创建后续记录———————————————————————*/while(1){if(head_flag==1) //此处的if与紧接着的else解决了刚处理的这一块是头还是别的什么东西的问题。 {head=p;}else //接上↑ {temp->next=p;}temp=p; //保存temp备用,表示最近一次处理的数据,可以用来当尾巴 p=(inf*)malloc(sizeof(inf)); //再申请一个结构体空间,开始下一轮输入printf("输入姓名,输入 exit 退出创建\n");gets(p->name); //输入姓名 if(strcmp(p->name,"exit")==0) //退出条件,此时退出要释放掉未连接的p {free(p);break;}else //正常创建接下来的记录 {printf("请输入电话\n");gets(p->number);printf("请输入地址\n");gets(p->address);printf("\n");head_flag=114514; //创建第二个记录后,标记解除 } }temp->next=NULL; //没有申请新空间,就在最近一次操作数据的后面堵住即可 return head; //返回头指针
}
void free_chain(inf* head) //链表释放函数 √
{inf* p;for(;p!=NULL;) // p作为中间变量,head移动后,释放p {p=head; if(head->next!=NULL) //接下来还有数据 {head=head->next;free(p);} else //接下来没有数据了 {free(head); return; }}
}
void print_chain(inf* head) //链表打印函数 √
{inf* p=head;int piece=0;printf("现有数据:\n");while(p!=NULL){printf("姓名 \n");puts(p->name);printf("电话 \n");puts(p->number);printf("地址 \n");puts(p->address);p=p->next;printf("\n");piece++;if(piece%4==0) //方便显示,一页四个 {printf("enter键翻页->->->->");getchar(); system("cls");}}
}
void search_by_address(inf* head) //地址关联查找,无返回√
{char address[100];inf* p=head;int num=0;if(head==NULL){printf("电话簿未创建\n");return; }printf("请输入地址\n");gets(address);while(1){if(strcmp(address,p->address)==0) //字符串比较 {num++; //查询数记录,同时也是是否查到的判断标志 printf("找到第%d个联系人:\n",num);print_this_node(p); //找到即打印 }p=p->next;if(p==NULL&&num==0){printf("查无此人\n");break;}if(p==NULL&&num!=0){break;} }return;
}
inf* search_by_number(inf* head) //号码查找,有返回 √
{char number[100];inf* p=head;if(head==NULL){printf("电话簿未创建\n");return NULL; }printf("请输入电话号码\n");gets(number);while(strcmp(number,p->number)!=0) //字符串比较 {p=p->next; //指向下个节点 if(p==NULL){printf("查无此人\n");return NULL;}}return p;
}
inf* search_by_name(inf* head) //姓名查找 ,有返回√
{char name[100];inf* p=head;if(head==NULL){printf("电话簿未创建\n");return NULL; }printf("请输入姓名\n");gets(name); //字符串比较 while(strcmp(name,p->name)!=0){p=p->next;if(p==NULL){printf("查无此人\n");return NULL;}}return p;
}
void search(inf* head) //查找功能选择以及打印函数√
{int mode;inf* result;while(1){printf("选择查找方式\n1.按照电话查找 2.按照姓名查找\n3.地址关联查找 4.退出\n");scanf("%d",&mode);getchar();switch(mode){case 1:result=search_by_number(head); //调用查找函数 ,找到某节点 print_this_node(result); //找到即打印 break;case 2:result=search_by_name(head); //调用查找函数 ,找到某节点 print_this_node(result); //找到即打印 break;case 3:search_by_address(head); //调用查找函数,这个不返回 break; case 4:return; //返回 break;default:printf("非法输入,请重新输入\n");break;}}
}
void modify_node(inf* head) //修改函数 √
{char temp[100];inf* p;int mode; //待修改节点指针while(1) //循环,可以修改多个,减少操作 {printf("选择一项已有的联系人信息以进行查找:\n 1.电话 2.姓名 3.不再修改\n");scanf("%d",&mode);getchar(); //保护switch switch(mode){case 1:p=search_by_number(head); //调用查找函数 break;case 2:p=search_by_name(head); //调用查找函数 break;case 3: //退出 return;break;default:printf("非法输入\n"); }if(p==NULL){continue; //找到了一个空,且在查找函数中已经输出了查无此人,跳过本次数据修改 }printf("已找到\n");print_this_node(p);int flag=1;while(flag==1){printf("选择一项以进行修改:\n 1.姓名 2.电话 3.地址 4.不修改\n");scanf("%d",&mode); //故技重施 getchar(); //老生常谈 switch(mode){case 1:printf("修改姓名为:");gets(p->name); break;case 2:printf("修改电话为:");gets(p->number); break;case 3:printf("修改地址为:");gets(p->address); break;case 4:flag++;break;default:printf("非法输入\n"); }printf("当前联系人信息:\n"); print_this_node(p);}} }
inf* sort_by_number(inf* head) //电话号码排序 ,返回一个有序链表的头指针 √
{inf* p=head; //保存head用于操作 inf temp; //用于在排序时,交换新链表的节点 int length=length_of_chain(head); //求原链表节点数 inf sorting[length]; //建立与链表相同大小的,连续结构体数组sortingfor(int i=0;i<length;i++) //将旧链表数据复制到新链表中,开始操作结构体数组 {strcpy(sorting[i].name,p->name);strcpy(sorting[i].number,p->number);strcpy(sorting[i].address,p->address);p=p->next;}for(int i=0;i<length-1;i++) {for(int j=0;j<length-i-1;j++) //使用字符串比较,进行排序 {if(strcmp(sorting[j].number,sorting[j+1].number)>0){temp=sorting[j]; //结构体交换 sorting[j]=sorting[j+1];sorting[j+1]=temp;}}} //排序完成p=head;//回到链表头for(int i=0;i<length;i++) //将结构体数组再存回到链表中 {strcpy(p->name,sorting[i].name);strcpy(p->number,sorting[i].number);strcpy(p->address,sorting[i].address);p=p->next;} return head; //直接返回排序后的结构体数组
}
inf* sort_by_name(inf* head) //姓名排序 ,返回一个有序链表的头指针 √
{inf* p=head; //保存head用于操作 inf temp; //用于在排序时,交换新链表的节点 int length=length_of_chain(head); //求原链表节点数 inf sorting[length]; //建立与链表相同大小的,连续结构体数组sortingfor(int i=0;i<length;i++) //将旧链表数据复制到新链表中,开始操作结构体数组 {strcpy(sorting[i].name,p->name);strcpy(sorting[i].number,p->number);strcpy(sorting[i].address,p->address);p=p->next;}for(int i=0;i<length-1;i++) {for(int j=0;j<length-i-1;j++) //使用字符串比较,进行排序 {if(strcmp(sorting[j].name,sorting[j+1].name)>0){temp=sorting[j]; //结构体交换 sorting[j]=sorting[j+1];sorting[j+1]=temp;}}} //排序完成p=head;//回到链表头for(int i=0;i<length;i++) //将结构体数组再存回到链表中 {strcpy(p->name,sorting[i].name);strcpy(p->number,sorting[i].number);strcpy(p->address,sorting[i].address);p=p->next;} return head; //直接返回排序后的结构体数组 }
void sort(inf* head) //排序功能选择、打印以及输出函数 √
{int mode;if(head==NULL){printf("电话簿为空\n");return; }printf("选择排序方式 1.按电话号码 2.按姓名\n"); scanf("%d",&mode); //梅开二度 getchar(); //三羊开泰 switch(mode){case 1:head=sort_by_number(head); //得到号码排序后链表头 break;case 2:head=sort_by_name(head); //得到姓名排序后链表头 break;default:printf("非法输入\n");break;}save_to_file(head); //将排序后链表存储 ,并展示
}
inf* load_chain(inf* head) //从文件加载到链表 √
{FILE* fp;if((fp=fopen("Contacts.dat","rb+"))==NULL){printf("电话簿打开失败\n");return NULL; //返回空指针,后续函数中接受空指针则跳过 }inf *p,*temp; //准备建立链表 p=(inf*)malloc(sizeof(inf)); //建立头节点空间if(fread(p,sizeof(inf),1,fp)==0) //能打开电话簿,但是读取到零个对象 ;无论成不成功,fp已经移动 {printf("电话簿为空\n");return head; } head=p; //确定头指针temp=p;p=(inf*)malloc(sizeof(inf)); //第二节点,之后开始链接 while(fread(p,sizeof(inf),1,fp)!=0) {temp->next=p; //链接 temp=p; //保存p=(inf*)malloc(sizeof(inf)); //新空间 }fclose(fp); //文件读完啦 temp->next=NULL; //链表尾free(p); //多出来一块p,释放return head;
}
void add_chain(inf* head) //链表添加 √
{inf* NEXT;inf* p=head;NEXT=creat_chain(); //调用链表创建函数,建立新链表 while(p->next!=NULL) //循环,找到旧链表的尾 {p=p->next;}p->next=NEXT; //将新旧链表链接
}
void delete_node(inf* head)
{inf* p;int i;int mode; //待修改节点指针while(1) //循环,可以修改多个,减少操作 {inf* temp=head;printf("查找要删除的联系人:\n 1.按电话查找 2.按姓名查找 3.结束删除\n");scanf("%d",&mode);getchar(); //保护switch switch(mode){case 1:p=search_by_number(head); //调用查找函数 break;case 2:p=search_by_name(head); //调用查找函数 break;case 3: //退出 return;break;default:printf("非法输入\n"); }if(p==NULL){continue; //找到了一个空,且在查找函数中已经输出了查无此人,跳过本次数据修改 }printf("已找到\n");print_this_node(p);int forward_length=length_of_chain(head)-length_of_chain(p); //获取修改点之前的长度printf("\n确认删除?1.删除 2.放弃:\n");scanf("%d",&mode); //故技重施 getchar(); //老生常谈 switch(mode){case 1:i=1;for(temp=head;i<forward_length;i++){temp=temp->next; //找到待删除节点之前的节点 }if(i==1){head=head->next;free(temp);}else{temp->next=p->next;free(p);} //释放该节点printf("已删除\n"); break;case 2:continue;break;default:printf("非法输入,请重新输入\n"); }print_chain(head);}
}
void save_to_file(inf* head) //将改动保存到文件 √
{FILE* fp;inf* p=head;if((fp=fopen("Contacts.dat","wb"))==NULL) //不涉及具体内容的打开失败 {printf("电话簿打开失败\n");return ; //返回}if(head==NULL) //涉及内容的文件为空 {printf("保存了空的电话簿\n");return ;}while(p!=NULL){if(fwrite(p,sizeof(inf),1,fp)!=1) //写入的同时进行判断 {printf("保存出错\n");return;}p=p->next;}fclose(fp); //关闭文件 printf("更改已保存\n"); system("pause");
}
3.头文件内容(Contacts.h)
typedef struct inf //结构体
{char name[100];char address[100];char number[100];struct inf* next;
} inf;
void print_this_node(inf* node); //节点打印函数 √
void ShowMenu(); //展示lby菜馆的菜单 √
int length_of_chain(inf* head); //求出链表(以节点数表示的)长度 √
inf* creat_chain(); //电话簿(链表)创建函数,返回链表头√
void free_chain(inf* head); //链表释放函数 √
void print_chain(inf* head); //链表打印函数 √
void search_by_address(inf* head); //地址关联查找,无返回 √
inf* search_by_number(inf* head); //号码查找,有返回 √
inf* search_by_name(inf* head); //姓名查找 ,有返回 √
void search(inf* head); //查找功能选择以及打印函数 √
void modify_node(inf* head); //修改函数 √
inf* sort_by_number(inf* head); //电话号码排序 ,返回一个有序链表的头指针
inf* sort_by_name(inf* head); //姓名排序 ,返回一个有序链表的头指针 √
void sort(inf* head); //排序功能选择、打印以及输出函数 √
inf* load_chain(inf* head); //从文件加载到链表 √
void add_chain(inf* head); //链表添加 √
void save_to_file(inf* head); //将改动保存到文件 √
void delete_node(inf* head); //节点删除 √
int judge(inf* head); //判断头节点是否为空,在主函数中使用√
总结:功能一个个做起来不是很难,就是实验要求分工,需要一定的协商,最重要的是耐心。
C语言实现简单电话簿(注释版)相关推荐
- 二叉树的非递归遍历算法C语言实现(详细注释版)
二叉树的非递归算法遍历分为:先序遍历,中序遍历,后序遍历. 此文章我会根据先.中.后的顺序为大家用C语言实现全部代码. 顾名思义先序遍历是先遍历根节点,随后是左孩子,右孩子 . 中序遍历与后序遍历可以 ...
- 线性表之顺序表基本操作(C语言实现,详细注释版)
有不懂的可以问我,把自己练习编写的代码在这里和大家分享下.如有错误欢迎指正. 编写不易,喜欢的话,点个赞吧
- perl语言入门第七版中文_python和c语言哪个简单
python相较C语言入门要简单的多. C语言是一门面向过程.抽象化的通用程序设计语言,广泛应用于底层开发.C语言能以简易的方式编译.处理低级存储器. C语言是仅产生少量的机器语言以及不需要任何运行环 ...
- C语言中怎么用循环统计买法,C语言入门谭浩强版简单选择法冒泡法用数组和for循环进行学生成绩简单统计处理...
C语言入门谭浩强版 简单选择法排序 冒泡法排序 用数组和for循环进行学生成绩简单统计处理 例如:求个人平均分 学科平均分 单科最高分等 简单选择法 #include #define N 10 voi ...
- C语言实现简单版Linux的cp命令
C语言实现简单版的Linux的cp命令 前言: 这是我的第一篇CSDN文章,写的不够好的地方有请各路大神指正.本人也是接触代码时间很短,在这里希望把自己学习到的知识写成一些有用的代码.后面有空的话 ...
- verilog 自动售货机状态机实现_基于Verilog语言的简单自动售货机-数电课设报告(最终版)最新版...
<基于Verilog语言的简单自动售货机-数电课设报告.docx>由会员分享,可免费在线阅读全文,更多与<基于Verilog语言的简单自动售货机-数电课设报告(最终版)>相关文 ...
- c语言英汉互译编程,用C语言编辑简单英汉互译词典.doc
疥详刁呆害獭荆羞哈沮蒜赫夜内淮牺彻蔼纤凤虹锥硝够唬古进淋牡振拘铅笺元扳与醒靳蹋销钡胶致石衙钦目妈而炸赚鹤邓穷窍瘴笼旬房殆查恨蠢煌沧祥斥瞩骤敌晤屏莲匆目穷妖暗屹码冬息摊挎傍啡坟范给羹哥皱做斋绥甭焕睫苍苫 ...
- 北华大学c语言题库百度云,北华大学C语言题库精简打印版(全).doc
北华大学C语言题库精简打印版(全).doc 北华大学C语言题库精简打印版(全)一.判断题 - 正确篇1.字符常量的长度肯定为1.Y2.在调用函数时,实参把值传送给对应位置上的形参,形参的值不能传给实参 ...
- c语言入门经典第五版自学,C语言入门经典(第5版) PDF扫描[103MB]
C语言入门经典(第5版) 内容简介: C语言是每一位程序员都应该掌握的基础语言.C语言是微软.NET编程中使用的C#语言的基础:C语言是iPhone.iPad和其他苹果设备编程中使用的Objecti ...
最新文章
- Java(CallableStatement)调用Oracle存储过程返回结果集(ResultSet)
- python 代理使用方法简介
- Django 中间件
- ArcGIS Engine 编辑介绍
- win8以上windows系统eclipse环境下图片显示乱码问题解决
- Android开发WebView之加载HTML源码修改HTML字体大小以及缩放HTML的方法
- getReadableDatabase VS getWritableDatabase
- 传输层TCP(流量控制和拥塞控制)
- OpenGL基础28:模型
- dapper框架_.net core 基于Dapper 的分库分表开源框架(core-data)
- 编写led驱动及其实验过程
- 2.PHP7内核剖析 --- SAPI
- 软件产品案例分析----K米app
- 微信小程序 图片上传预览删除
- linux个人游戏服务器搭建,linux游戏服务器搭建(一)
- Unity Decal 贴花效果测试
- -bash: netstat: 未找到命令
- 解决Windows10 系统中桌面窗口管理器运行时占用内存过大的问题
- Esxi6.7安装TinyCoreLinux
- Java8实战读书笔记-第3章 λ表达式