实现通讯录能使我们进一步加深对顺序表的理解,接下来就由本哲♂学家带你手把手实现通信录。
其中需要用到顺序表的知识可以点击下面链接了解:http://t.csdnimg.cn/9SjGd话不多说,我们♂开始吧。
一、通讯录头文件声明
由于我们前面已经写过顺序表其头文件我们可以直接用这里只展示seqlist的头文件
seqlist.h:
#pragma once//防止头文件二次引用
typedef struct PersonInfo SLDataType;//方便后续该存储类型
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include"contact.h"
typedef struct seqlist
{
SLDataType* arr;//存放数据的数组
int size;//有效元素个数
int capacity;//空间的大小
}SL;
// 初始化和销毁
void SLInit(SL * ps);
void SLDestroy(SL* ps);
void SLPrint(SL* ps);
//扩容
void SLCheckCapacity(SL* ps);
//头部插入删除 / 尾部插入删除
void SLPushBack(SL* ps, SLDataType x);
void SLPopBack(SL* ps);
void SLPushFront(SL* ps, SLDataType x);
void SLPopFront(SL* ps);
//指定位置之前插入/删除数据
void SLInsert(SL* ps, int pos, SLDataType x);
void SLErase(SL* ps, int pos);
//找到指定位置的数据
int SLFind(SL* ps, SLDataType x);
contact.h:
#pragma once
#define NAME_MAX 100
#define SEX_MAX 4
#define TEL_MAX 11
#define ADDR_MAX 100
//因为头文件不能相互包含,所以用前置声明
typedef struct SeqList 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 DelContact(contact* con);
//展示通讯录数据
void ShowContact(contact* con);
//查找通讯录数据
void FindContact(contact* con);
//修改通讯录数据
void ModifyContact(contact* con);
//销毁通讯录数据
void DestroyContact(contact* con);
值得注意的是因为头文件不能相互包含所以只能前置声明我们创建的SeqList结构体。
二、函数实现:
首先我们要明确通讯录实际上是顺序表,只不过其内容是结构,对通讯录的操作,实际上是对顺序表的操作。
1、初始化与销毁
前面我们知道,通讯录本质上是顺序表,所以对通讯录的初始化和销毁,实际上就是对顺序表的初始化和销毁:
//初始化通讯录
void InitContact(contact* con)//实际上是对顺序表初始化
{
SLInit(con);
}
//销毁通讯录
void DestroyContact(contact* con)
{
void SLDestroy(SL * ps);
}
2、添加通讯录信息
//添加通讯录信息
void ContactAdd(contact* con)
{
//获取用户输入的内容:姓名+性别+年龄+电话+地址
PeoInfo info;//创建顺序表的元素
printf("请输入要添加的联系人姓名:\n");
scanf("%s", info.name);
printf("请输入要添加的联系人性别:\n");
scanf("%s", info.sex);
printf("请输入要添加的联系人年龄:\n");
scanf("%d", &info.age);
printf("请输入要添加的联系人电话:\n");
scanf("%s", info.tel);
printf("请输入要添加的联系人住址:\n");
scanf("%s", info.addr);
//往通讯录中添加联系人数据,实际上就是尾部插入。
SLPushBack(con, info);//SLPushBack中带有空间大小检查功能这不用考虑空间大小问题
}
实际上,我们只不过将顺序表的成员改成了结构体而已,所以要将结构体插入(一定要捋清楚这个关系)。
3、删除通讯录信息
如果是一个普通的数组,我们只需要将要删除的下标穿过去就好了,但是这个是要删除结构体,我们可以通过访问结构体包含的名字成员对比,来找到元素下标删除数据:
//只是删除数据的辅助函数,不是找数据的函数
int FindByName(contact* con, char name[])
{
for (int i = 0; i < con->size; i++)
{
if (0 == strcmp(con->arr[i].name, name))
{
//找到了
return i;
}
}
//没有找到
return -1;
}
//删除通讯录数据
void DelContact(contact* con)
{
printf("请输入要删除的名字:");
char dename[NAME_MAX];
scanf("%s", dename);
int find = FindByName(con, dename);
if (find < 0)
{
printf("要删除的联系人数据不存在!\n");
return;//不存在直接跳出函数;
}
//要删除的联系人数据存在--->知道了要删除的联系人数据对应的下标
SLErase(con, find);
printf("删除成功!\n");
}
代码显示没有任何问题。
4、展示通讯录数据
这个就直接上代码了,非常简单
void ShowContact(contact* con)
{
//表头:姓名 性别 年龄 电话 地址
printf("%s %s %s %s %s\n", "姓名", "性别", "年龄", "电话", "地址");
//遍历通讯录,按照格式打印每个联系人数据
for (int i = 0; i < con->size; i++)
{
printf("%3s %3s %3d %3s %3s\n", //手动调整一下格式
con->arr[i].name,
con->arr[i].sex,
con->arr[i].age,
con->arr[i].tel,
con->arr[i].addr
);
}
}
5、查找通讯录数据
整体思路和前面的删除数据的辅助函数十分类似,或者直接可以使用上面的辅助函数。直接上代码:
void FindContact(contact* con)
{
char name[NAME_MAX];
printf("请输入要查找的联系人姓名\n");
scanf("%s", name);
int find = FindByName(con, name);
if (find < 0)
{
printf("要查找的联系人数据不存在!\n");
return;
}
printf("%s %s %s %s %s\n", "姓名", "性别", "年龄", "电话", "地址");
printf("%3s %3s %3d %3s %3s\n",
con->arr[find].name,
con->arr[find].sex,
con->arr[find].age,
con->arr[find].tel,
con->arr[find].addr
);
}
同样的代码没有任何问题。
6、修改指定数据
与删除数据时整体思路一致,直接上代码:
void ModifyContact(contact* con)
{
char name[NAME_MAX];
printf("请输入要查找的联系人姓名\n");
scanf("%s", name);
int find = FindByName(con, name);
if (find < 0)
{
printf("要查找的联系人数据不存在!\n");
return;
}
//直接修改不用创建临时变量
printf("请输入要修改的联系人姓名:\n");
scanf("%s", con->arr[find].name);
printf("请输入要修改的联系人性别:\n");
scanf("%s", con->arr[find].sex);
printf("请输入要修改的联系人年龄:\n");
scanf("%d", &con->arr[find].age);
printf("请输入要修改的联系人电话:\n");
scanf("%s", con->arr[find].tel);
printf("请输入要修改的联系人住址:\n");
scanf("%s", con->arr[find].addr);
}
代码没有任何问题
7、关于永久保存问题
以上所写的信息在退出程序后是无法保存的,要永久保存我们得使用文件操作函数来达到这个目的。
1、信息导入:
//从文件中读取数据
void LoadContact(contact* con)
{
FILE* pf = fopen("contact.txt", "rb");
if (pf == NULL) {
perror("fopen error!\n");
return;
}
//循环读取⽂件数据
PeoInfo info;
while (fread(&info, sizeof(PeoInfo), 1, pf))
{
SLPushBack(con, info);
}
printf("历史数据导入通讯录成功!\n");
}
值得注意的一点是fread导入的是二进制文件所以我们是无法修改文件达到导入信息的目的
2、信息保存
void SaveContact(contact* con)
{
FILE* pf = fopen("contact.txt", "wb");
if (pf == NULL) {
perror("fopen error!\n");
return;
}
//将通讯录数据写⼊⽂件
for (int i = 0; i < con->size; i++)
{
fwrite(con->arr + i, sizeof(PeoInfo), 1, pf);
}
printf("通讯录数据保存成功!\n");
}
8、控制台的实现
#include"seqlisr.h"
//#include"contact.h"
void menu()
{
printf("******************通讯录******************\n");
printf("*******1.增加联系人 2.删除联系人********\n");
printf("*******3.修改联系人 4.查找联系人********\n");
printf("*******5.展示联系人 6.保存数据 ********\n");
printf("*******7.导入数据 0. 退出 *********\n");
printf("******************************************\n");
}
int main()
{
int op = -1;
contact con;
InitContact(&con);
do {
menu();
printf("请选择您的操作:\n");
scanf("%d", &op);
//要根据对应的op执行不同的操作
switch (op)
{
case 1:
AddContact(&con);
break;
case 2:
DestroyContact(&con);
break;
case 3:
ModifyContact(&con);
break;
case 4:
FindContact(&con);
break;
case 5:
ShowContact(&con);
break;
case 6:
SaveContact(&con);
break;
case 7:
LoadContact(&con);
break;
case 0:
printf("退出通讯录....\n");
break;
default:
printf("输入错误,请重新选择您的操作!\n");
break;
}
} while (op != 0);
DelContact(&con);
return 0;
}
第一次运行:
第二次运行:
发现数据被永久保存了。
总结:
通讯录是非常典型的顺序表使用示例,其最后的永久保存数据更是需要我们使用文件函数,代码总量也是非常大足足有400多行,需要大家好好消化。期待下一次和你们一起van♂。