个人主页:秋风起,再归来~
文章专栏:C语言实战项目
个人格言:悟已往之不谏,知来者犹可追
克心守己,律己则安!
1、前言
友友们,这篇文章是基于单链表来实现通讯管理系统的,所以一定要先看完我之前写过的一篇关于单链表的实现(文章链接)的文章哦~
其实基于单链表实现通讯录的思路与基于顺讯表实现通讯录的思路是一样的,在这里我就不进行赘述了。如果还没有看过我之前写的一篇基于顺序表实现通讯录(文章链接)的宝子们一定要去看看哦~
2、各种接口的实现
以下是我们希望实现的接口~
//contact.h
#pragma once
#define NAME_MAX 100
#define SEX_MAX 4
#define TEL_MAX 11
#define ADDR_MAX 100
//前置声明
typedef struct SListNode contact;
//用户数据
typedef struct PersonInfo
{
char name[NAME_MAX];
char sex[SEX_MAX];
int age;
char tel[TEL_MAX];
char addr[ADDR_MAX];
}PeoInfo;
//初始化通讯录
void InitContact(contact** con);
//添加通讯录数据
void AddContact(contact** con);
//展示通讯录数据
void ShowContact(contact* con);
//删除通讯录数据
void DelContact(contact** con);
//查找通讯录数据
void FindContact(contact* con);
//修改通讯录数据
void ModifyContact(contact** con);
//销毁通讯录数据
void DestroyContact(contact** con);
2.1 初始化通讯录
我们希望在初始化通讯录时导入之前我们原有文件中的数据,那我们就可以进行如下的操作~
//从文件中导入原数据
void DLoadContact(contact** con)
{
FILE* pf = fopen("contact.txt", "rb");
if (pf == NULL)
{
perror("open fail\n");
return;
}
PeoInfo p = { 0 };
while (fread(&p, sizeof(PeoInfo),1,pf))
{
SListPushBack(con, p);
}
printf("历史数据导入成功!\n");
fclose(pf);
pf = NULL;
}
在初始化通讯录之后加载数据!
//初始化通讯录
void InitContact(contact** con)
{
assert(con);
(*con) = NULL;
DLoadContact(con);
}
2.2 添加通讯录数据
//添加通讯录数据
void AddContact(contact** con)
{
assert(con);
PeoInfo p = {0};
printf("请输入添加联系人的姓名:");
scanf("%s", p.name);
printf("请输入添加联系人的性别:");
scanf("%s", p.sex);
printf("请输入添加联系人的年龄:");
scanf("%d", &(p.age));
printf("请输入添加联系人的电话:");
scanf("%s", p.tel);
printf("请输入添加联系人的住址:");
scanf("%s", p.addr);
SListPushBack(con,p);
printf("\n");
}
2.3 删除通讯录数据
在查找通讯录数据之外封装一个函数(findByName)来通过名字查找联系人!
(因为我们在之后的查找和修改接口中都会用到这个方法,所以我们把它封装成一个函数方便我们后续的使用!)
//通过名字查找联系人
contact* findByName(contact* con,char* name)
{
assert(con);
contact* cur = con;
while (cur)
{
if (strcmp(cur->data.name, name) == 0)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
在原有单链表的删除指定数据的接口上进行封装(让单链表摇身一变成为通讯录!)
//删除通讯录数据
void DelContact(contact** con)
{
assert(con && (*con));
char name[NAME_MAX];
printf("请输入你要删除的联系人的名字:");
scanf("%s", name);
contact* pos = findByName(*con,name);
if (pos == NULL)
{
printf("您要删除的联系人不存在!\n");
return;
}
SListErase(con, pos);
printf("删除成功!\n");
}
2.4 展示通讯录数据
遍历我们的通讯录打印信息!
//展示通讯录数据
void ShowContact(contact* con)
{
assert(con);
contact* cur = con;
printf("名字\t\t性别\t\t年龄\t\t电话\t\t住址\n");//打印表头
while (cur)
{
printf("%s\t\t%s\t\t%d\t\t%s\t\t%s\n",
cur->data.name,
cur->data.sex,
cur->data.age,
cur->data.tel,
cur->data.addr);
cur = cur->next;
}
}
2.5 查找通讯录数据
//通过名字查找联系人
contact* findByName(contact* con,char* name)
{
assert(con);
contact* cur = con;
while (cur)
{
if (strcmp(cur->data.name, name) == 0)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
在接口内部调用该函数!
//查找通讯录数据
void FindContact(contact* con)
{
assert(con);
char name[NAME_MAX];
printf("请输入你要查找的联系人的名字:");
scanf("%s", name);
contact* ret = findByName(con, name);
if (ret == NULL)
{
printf("您要查找的联系人不存在!\n");
return;
}
printf("找到了!\n");
printf("名字\t\t性别\t\t年龄\t\t电话\t\t住址\n");//打印表头
printf("%s\t\t%s\t\t%d\t\t%s\t\t%s\n",
ret->data.name,
ret->data.sex,
ret->data.age,
ret->data.tel,
ret->data.addr);
}
2.6 修改通讯录数据
//通过名字查找联系人
contact* findByName(contact* con,char* name)
{
assert(con);
contact* cur = con;
while (cur)
{
if (strcmp(cur->data.name, name) == 0)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
在接口内部调用该函数!
//修改通讯录数据
void ModifyContact(contact** con)
{
assert(con);
char name[NAME_MAX];
printf("请输入你要修改的联系人的名字:");
scanf("%s", name);
contact* ret = findByName(*con, name);
if (ret == NULL)
{
printf("您要修改的联系人不存在!\n");
return;
}
PeoInfo p = { 0 };
printf("请输入修改后联系人的姓名:");
scanf("%s", p.name);
printf("请输入修改后联系人的性别:");
scanf("%s", p.sex);
printf("请输入修改后加联系人的年龄:");
scanf("%d", &(p.age));
printf("请输入修改后联系人的电话:");
scanf("%s", p.tel);
printf("请输入修改后联系人的住址:");
scanf("%s", p.addr);
SListModify(ret, p);
printf("修改成功!\n");
}
2.7 销毁通讯录数据
因为我们希望在退出通讯管理系统的时候可以将我们的操作都保留下来,所以我们可以对其进行文件操作!
//将输入的数据保存到文件中
void SaveContact(contact** con)
{
FILE* pf = fopen("contact.txt", "wb");
if (pf == NULL)
{
perror("open fail!\n");
return;
}
contact* cur = *con;
while (cur)
{
fwrite(&(cur->data), sizeof(PeoInfo), 1, pf);
cur = cur->next;
}
printf("历史数据保存成功!\n");
fclose(pf);
pf = NULL;
}
在销毁通讯录之前保留数据!
//销毁通讯录数据
void DestroyContact(contact** con)
{
SaveContact(con);
SListDestroy(con);
}
3、完整源码
SeqList.h
#pragma once//避免头文件被多次引用
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include"Contact.h"
typedef PeoInfo SListDataType;//便于改变数据类型
//定义一个结构体类型的节点
typedef struct SListNode
{
SListDataType data;
struct SListNode* next;//存储下一个节点的地址
}SListNode;
//1. 新节点的创建
SListNode* SListCreatNode(SListDataType x);
//2. 打印单链表
void PrintSList(SListNode* phead);
//3. 头插
void SListPushFront(SListNode** phead, SListDataType x);
//4. 头删
void SListPopFront(SListNode** phead);
//5. 尾差
void SListPushBack(SListNode** phead, SListDataType x);
//6. 尾删
void SListPopBack(SListNode** phead);
//7. 查找元素X
SListNode* SListFind(SListNode* phead, SListDataType x);
//8. 在pos位置修改
void SListModify(SListNode* pos, SListDataType x);
//9. 在任意位置之前插入
void SListInsert(SListNode** phead, SListNode* pos, SListDataType x);
//10. 在任意位置删除
void SListErase(SListNode** phead, SListNode* pos);
//11. 销毁单链表
void SListDestroy(SListNode** phead);
Contact.h
#define _CRT_SECURE_NO_WARNINGS
//contact.h
#pragma once
#define NAME_MAX 100
#define SEX_MAX 4
#define TEL_MAX 11
#define ADDR_MAX 100
//前置声明
typedef struct SListNode contact;
//用户数据
typedef struct PersonInfo
{
char name[NAME_MAX];
char sex[SEX_MAX];
int age;
char tel[TEL_MAX];
char addr[ADDR_MAX];
}PeoInfo;
//初始化通讯录
void InitContact(contact** con);
//添加通讯录数据
void AddContact(contact** con);
//展示通讯录数据
void ShowContact(contact* con);
//删除通讯录数据
void DelContact(contact** con);
//查找通讯录数据
void FindContact(contact* con);
//修改通讯录数据
void ModifyContact(contact** con);
//销毁通讯录数据
void DestroyContact(contact** con);
SeqList.c
#include"SList.h"
//1. 新节点的创建
SListNode* SListCreatNode(SListDataType x)
{
SListNode* NewNode = (SListNode*)malloc(sizeof(SListNode));//开辟空间
if (NewNode == NULL)//判断空间是否开辟成功
{
perror("malloc fail");
return NULL;
}
NewNode->data = x;//赋值
NewNode->next = NULL;//置空
return NewNode;
}
#if 0
//2. 打印单链表
void PrintSList(SListNode* phead)
{
if (phead == NULL)
{
printf("NULL");//如果链表没有元素就打印NULL
return;
}
SListNode* cur = phead;
//循环单链表打印
while (cur != NULL)
{
printf("%d->", cur->data);
cur = cur->next;
}
printf("NULL\n");
}
#endif
//3. 头插
void SListPushFront(SListNode** phead, SListDataType x)
{
assert(phead);
SListNode* newnode = SListCreatNode(x);//创建一个新节点
newnode->next = *phead;
*phead = newnode;
}
//4. 头删
void SListPopFront(SListNode** phead)
{
assert(phead);
assert(*phead);//如果没有数据就不用头删,并报错
SListNode* cur = (*phead)->next;
free(*phead);
*phead = cur;
}
//5. 尾插
void SListPushBack(SListNode** phead, SListDataType x)
{
assert(phead);
if (*phead == NULL)
{
*phead = SListCreatNode(x);//创建新节点并插入
}
else
{
SListNode* tail = *phead;
while (tail->next != NULL)//找到尾节点
{
tail = tail->next;
}
tail->next = SListCreatNode(x);//创建新节点并插入
}
}
//6. 尾删
void SListPopBack(SListNode** phead)
{
assert(phead);
assert(*phead);//链表为空就不进行尾删
SListNode* tail = *phead;
if (tail->next == NULL)//如果链表就只有一个元素就进行头删
{
SListPopFront(phead);
}
else
{
while (tail->next->next != NULL)
{
tail = tail->next;
}
free(tail->next);
tail->next = NULL;
}
}
#if 0
//7. 查找元素X
SListNode* SListFind(SListNode* phead, SListDataType x)
{
assert(phead);
while (phead->next != NULL)//注意最后一个节点是没有查找的
{
if (phead->data == x)
return phead;
phead = phead->next;
}
if (phead->data == x)
return phead;//最后一个节点没有查找
else
return NULL;//没找到
}
#endif
//8. 在pos位置修改
void SListModify(SListNode* pos, SListDataType x)
{
assert(pos);
pos->data = x;
}
//9. 在任意位置之前插入
void SListInsert(SListNode** phead, SListNode* pos, SListDataType x)
{
assert(phead);
assert(*phead);
if (pos == *phead)//如果pos位置刚好是第一个节点就进行头插
{
SListPushFront(phead, x);
}
else
{
SListNode* newnode = SListCreatNode(x);
SListNode* cur = *phead;
while (cur->next != pos)//找到pos前一个节点
{
cur = cur->next;
}
cur->next = newnode;
newnode->next = pos;
}
}
//10. 在任意位置删除
void SListErase(SListNode** phead, SListNode* pos)
{
assert(phead && *phead && pos);
if (pos == *phead)//如果pos位置就是第一个节点就进行头删
{
SListPopFront(phead);
}
else
{
SListNode* cur = *phead;
while (cur->next != pos)//找到pos前一个节点
{
cur = cur->next;
}
cur->next = pos->next;
free(pos);
}
}
//11. 销毁单链表
void SListDestroy(SListNode** phead)
{
assert(*phead && phead);
SListNode* cur = *phead;
while (cur != NULL)
{
SListNode* tmp = cur->next;
free(cur);
cur = tmp;
}
*phead = NULL;
}
Contact.c
#include"SList.h"
//从文件中导入原数据
void DLoadContact(contact** con)
{
FILE* pf = fopen("contact.txt", "rb");
if (pf == NULL)
{
perror("open fail\n");
return;
}
PeoInfo p = { 0 };
while (fread(&p, sizeof(PeoInfo),1,pf))
{
SListPushBack(con, p);
}
printf("历史数据导入成功!\n");
fclose(pf);
pf = NULL;
}
//初始化通讯录
void InitContact(contact** con)
{
assert(con);
(*con) = NULL;
DLoadContact(con);
}
//添加通讯录数据
void AddContact(contact** con)
{
assert(con);
PeoInfo p = {0};
printf("请输入添加联系人的姓名:");
scanf("%s", p.name);
printf("请输入添加联系人的性别:");
scanf("%s", p.sex);
printf("请输入添加联系人的年龄:");
scanf("%d", &(p.age));
printf("请输入添加联系人的电话:");
scanf("%s", p.tel);
printf("请输入添加联系人的住址:");
scanf("%s", p.addr);
SListPushBack(con,p);
printf("\n");
}
//展示通讯录数据
void ShowContact(contact* con)
{
assert(con);
contact* cur = con;
printf("名字\t\t性别\t\t年龄\t\t电话\t\t住址\n");//打印表头
while (cur)
{
printf("%s\t\t%s\t\t%d\t\t%s\t\t%s\n",
cur->data.name,
cur->data.sex,
cur->data.age,
cur->data.tel,
cur->data.addr);
cur = cur->next;
}
}
//通过名字查找联系人
contact* findByName(contact* con,char* name)
{
assert(con);
contact* cur = con;
while (cur)
{
if (strcmp(cur->data.name, name) == 0)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
//删除通讯录数据
void DelContact(contact** con)
{
assert(con && (*con));
char name[NAME_MAX];
printf("请输入你要删除的联系人的名字:");
scanf("%s", name);
contact* pos = findByName(*con,name);
if (pos == NULL)
{
printf("您要删除的联系人不存在!\n");
return;
}
SListErase(con, pos);
printf("删除成功!\n");
}
//查找通讯录数据
void FindContact(contact* con)
{
assert(con);
char name[NAME_MAX];
printf("请输入你要查找的联系人的名字:");
scanf("%s", name);
contact* ret = findByName(con, name);
if (ret == NULL)
{
printf("您要查找的联系人不存在!\n");
return;
}
printf("找到了!\n");
printf("名字\t\t性别\t\t年龄\t\t电话\t\t住址\n");//打印表头
printf("%s\t\t%s\t\t%d\t\t%s\t\t%s\n",
ret->data.name,
ret->data.sex,
ret->data.age,
ret->data.tel,
ret->data.addr);
}
//修改通讯录数据
void ModifyContact(contact** con)
{
assert(con);
char name[NAME_MAX];
printf("请输入你要修改的联系人的名字:");
scanf("%s", name);
contact* ret = findByName(*con, name);
if (ret == NULL)
{
printf("您要修改的联系人不存在!\n");
return;
}
PeoInfo p = { 0 };
printf("请输入修改后联系人的姓名:");
scanf("%s", p.name);
printf("请输入修改后联系人的性别:");
scanf("%s", p.sex);
printf("请输入修改后加联系人的年龄:");
scanf("%d", &(p.age));
printf("请输入修改后联系人的电话:");
scanf("%s", p.tel);
printf("请输入修改后联系人的住址:");
scanf("%s", p.addr);
SListModify(ret, p);
printf("修改成功!\n");
}
//将输入的数据保存到文件中
void SaveContact(contact** con)
{
FILE* pf = fopen("contact.txt", "wb");
if (pf == NULL)
{
perror("open fail!\n");
return;
}
contact* cur = *con;
while (cur)
{
fwrite(&(cur->data), sizeof(PeoInfo), 1, pf);
cur = cur->next;
}
printf("历史数据保存成功!\n");
fclose(pf);
pf = NULL;
}
//销毁通讯录数据
void DestroyContact(contact** con)
{
SaveContact(con);
SListDestroy(con);
}
这里我就没有单独去写一个菜单了,本身的意义也并不大,但是如果友友们想写一个来玩一玩,可以看看我之前那篇基于顺序表实现通讯录(文章链接),那里有菜单的模版(只要改几个接口的名字就行了)~
4、 完结散花
好了,这期的分享到这里就结束了~
如果这篇博客对你有帮助的话,可以用你们的小手指点一个免费的赞并收藏起来哟~
如果期待博主下期内容的话,可以点点关注,避免找不到我了呢~
我们下期不见不散~~