前两章我们已经知道顺序表的功能并完成了功能实现,下面我们将实现通讯录的以下功能:
首先我们要对所用文件进行分类管理,以便后续操作。
我们将要在顺序表的基础上增加“contact.h”头文件和“contact.c”功能文件 注意头文件的正确引用 如下
请仔细检查,这是完成较大项目的基础。不同文件管理不同内容,方便快捷,效率高。
接下来是联系人的数据管理,我们需要改变原本顺序表的“SLDataType”为一个新类型。*我们可以想到联系人的信息不一,所以我们使用结构体来管理数据。
#define NAME_MAX 100
#define SEX_MAX 4
#define TEL_MAX 20
#define ADDR_MAX 200
//前置声明
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;
我们用宏定义常量,方便后续查看修改。这里我设置了姓名 性别 年龄 号码 地址五种信息。代码中“前置声明”是为了避免后续引用出现问题。我们通过“typedef”进行重命名,方便后续书写代码。
上面将我们的准备工作进行完毕,下面开始实现功能。我们基于顺序表在进行操作。如有不理解的地方请参考【通讯录项目 (2 / 3)】,下面不对 顺序表功能 进行详细说明 我们会使用顺序表大多数功能,请理解顺序表的功能在进行阅读。
“初始化”只需要简单的引用顺序表的初始化即可。
void InitContact(contact* con)
{
SLInit(con);
}
void SLInit(SL* ps)
{
assert(ps);//断言判断是否为空
ps->a = NULL;//将初始地址改为空
ps->size = ps->capacity = 0;//容量与大小定为空
}
这样就实现了初始化,与初始化顺序表一致。
添加联系人也非常简单,只需要依次输入数据,在进行顺序表的插入即可。
void AddContact(contact* con)
{
assert(con);
PeoInfo info;
printf("请输入联系人姓名:\n");
scanf("%s", info.name);
printf("请输入联系人的性别:\n");
scanf("%s", info.sex);
printf("请输入联系人的年龄:\n");
scanf("%d", &info.age); //这里需要取地址,因为age是int类型,其余为数组
printf("请输入联系人的电话:\n");
scanf("%s", info.tel);
printf("请输入联系人的住址:\n");
scanf("%s", info.addr);
SLPushBack(con,info);
printf("添加成功\n");
}
void SLPushBack(SL* ps, SLDataType x)
{
assert(ps);
SLCheckCapacity(ps);
ps->a[ps->size++] = x;
}
这样就进行了数据的加入。
删除联系人依然使用顺序表的删除功能。这里需要一个"查找联系人"的功能。需要通过一个信息来查找联系人是否存在。
我们输入一个信息,来进行遍历查找联系人。返回目标的偏移值。
//查找联系人
int findname(contact* con, char name[NAME_MAX])
{
assert(con);
for (int i = 0; i < con->size; i++)
{
if (strcmp(con->a[i].name, name) == 0)
{
return i;
}
}
return -1;
}
同样使用顺序表的删除功能来实现,通过“查找联系人”返回的偏移值来进行定位
void DelContact(contact* con)
{
assert(con);
char find[NAME_MAX] = { 0 };
printf("请输入你想删除的联系人姓名:> \n");
scanf("%s",find);
int ret = findname(con, find);
if (ret < 0)
{
printf("未找到该联系人\n");
}
else
SLErase(con, ret);
}
void SLErase(SL*ps,int pos)
{
assert(ps);
if (pos >= 0 && pos < ps->size)
{
for (int i = pos; i < ps->size - 1; i++)
{
ps->a[i] = ps->a[i + 1];//a[size-2]=a[size-1]模拟最后一次查找
}
ps->size--;
}
else
assert(pos);
}
这样通过覆盖就完成了删除功能。
展示通讯录的功能是对顺序表展示的扩展。
void ShowContact(contact* pcon) {
//打印通讯录所有的数据
//先打印表头文字
assert(pcon);
printf("%s %s %s %s %s\n", "姓名", "性别", "年龄", "电话", "住址");
for (int i = 0; i < pcon->size; i++)
{
printf("%-4s %-4s %-4d %-11s %-4s\n",
pcon->a[i].name,
pcon->a[i].sex,
pcon->a[i].age,
pcon->a[i].tel,
pcon->a[i].addr
);
}
}
只需遍历顺序表,并打印联系人数据即可。 注意换行符,与打印格式让界面更加美观。
查找与修改操作相似,我们放在一起说明
查找联系人以上面的查找目标功能为基础,通过遍历进行查找。这里需要进行一次打印操作来展示查找结果
void FindContact(contact* con)
{
assert(con);
char find[NAME_MAX] = { 0 };
printf("请输入想要查找的联系人姓名:> \n");
scanf("%s",find);
int ret = findname(con, find);
if (ret < 0)
{
printf("未找到该联系人\n");
}
else{
printf("找到了\n");
printf("%s %s %s %s %s\n", "姓名", "性别", "年龄", "电话", "住址");
printf("%-4s %-4s %-4d %-4s %-4s\n",
con->a[ret].name,
con->a[ret].sex,
con->a[ret].age,
con->a[ret].tel,
con->a[ret].addr
);
}
}
修改联系人同样基于“查找目标”功能的基础,并进行一次修改操作。
oid ModifyContact(contact* con)
{
assert(con);
int name[NAME_MAX] = { 0 };
printf("请输入想修改的联系人: > \n");
scanf("%s", name);
int ret = findname(con, name);
//通过偏移量进行修改
printf("请输入联系人姓名:\n");
scanf("%s", con->a[ret].name);
printf("请输入联系人的性别:\n");
scanf("%s", con->a[ret].sex);
printf("请输入联系人的年龄:\n");
scanf("%d", &con->a[ret].age);
printf("请输入联系人的电话:\n");
scanf("%s", con->a[ret].tel);
printf("请输入联系人的住址:\n");
scanf("%s", con->a[ret].addr);
printf("修改完成\n");
ShowContact(con);
}
销毁功能是对储存空间的操作,通过free来实现销毁。
void DestroyContact(contact* con)
{
assert(con);
free(con->a);
con->size = con->capacity = 0;
con->a = NULL;
}
上面我们已经实现了通讯录的大部分功能,还有一些额外功能没有加入,比如读取文件数据,储存到文件等。 下面我们开始完善界面内容,来把通讯录的功能进行整合。
这个容易实现,按照喜好设置即可,下面给予参考
void menu()
{
printf("*************************************\n");
printf("********* 通讯录 **********\n");
printf("**** 1.添加联系人 2.删除联系人 *****\n");
printf("**** 3.修改联系人 4.查找联系人 *****\n");
printf("**** 5.查看通讯录 0.退出通讯录 *****\n");
}
效果如图
通过switch语句我们可以方便的完成功能选择
int main()
{
int op = -1;
contact con;
InitContact(&con);
do {
menu();
printf("请选择你的操作:> \n");
scanf("%d",&op);
switch (op)
{
case 1:
AddContact(&con);
break;
case 2:
DelContact(&con);
break;
case 3:
ModifyContact(&con);
break;
case 4:
FindContact(&con);
break;
case 5:
ShowContact(&con);
break;
case 0:
printf("goodbye~\n");
break;
default:
printf("输入格式有误,重新输入:> \n");
break;
}
} while (op != 0);
DestroyContact(&con);
return 0;
}
这样我们就完成了功能选择,都是有一点点瑕疵。
通过上面的菜单我们在进行几步操作后便会发现
界面非常冗杂混乱,如何解决呢。 我们可以通过
system("pause");
system("cls");
这两行简单的代码便可以大大优化界面
int main()
{
int op = -1;
contact con;
InitContact(&con);
do {
menu();
printf("请选择你的操作:> \n");
scanf("%d",&op);
switch (op)
{
case 1:
AddContact(&con);
system("pause");
system("cls");
break;
case 2:
DelContact(&con);
system("pause");
system("cls");
break;
case 3:
ModifyContact(&con);
system("pause");
system("cls");
break;
case 4:
FindContact(&con);
system("pause");
system("cls");
break;
case 5:
ShowContact(&con);
system("pause");
system("cls");
break;
case 0:
printf("goodbye~\n");
system("pause");
system("cls");
break;
default:
printf("输入格式有误,重新输入:> \n");
system("pause");
system("cls");
break;
}
} while (op != 0);
DestroyContact(&con);
return 0;
}
让我们看看效果
无论进行多少操作,我们的界面依然清爽。