在本篇之前的顺序表专题我们已经学习的顺序表的实现,了解了如何实现顺序表的插入和删除等功能,那么在本篇当中就要学习基于顺序表来实现通讯录,在通讯录当中能实现联系人的增、删、查改等功能,接下来就让我们一起来实现通讯录吧!
1.实现通讯录前的分析
在实现通讯录的代码前我们要先思考在通讯录项目中能实现什么样的功能
(1)至少能够存储100个人的通讯信息
(2)能够保存用户信息:名字、性别、年龄、电话、地址等
(3)增加联系人信息
(4)删除指定联系人
(5)查找制定联系人
(6)修改指定联系人
(7)显示联系人信息
同时由于在之前的顺序表中使用的是动态顺序表,所以在实现通讯录项目中也基于的是动态顺序表
以下是该通讯录项目的程序文件设置以及各文件中所实现的内容
2.通讯录的实现
2.1 联系人信息的设置以及顺序表内要做出的更改
由于顺序表的底层就是数组,所以我们就是要利用数组来实现如以下所示的结构
在通讯录中由于我们要存储的是多个联系人的信息,因此要定义一个结构体来存储联系人的信息
以下定义结构体struct PersonIfon来存储联系人的信息,并且使用typedef将该结构体重命名为Persondef
并且在PersonIfon中的每个数组的大小用#define来定义
#define MAX_NAME 20
#define MAX_GENDER 10
#define MAX_TELE 20
#define MAX_ADRESS 50
typedef struct PersonIfon//联系人信息
{
char name[MAX_NAME];//姓名
char Gender[MAX_GENDER];//性别
int age;//年龄
char Tele[MAX_TELE];//电话
char Adress[MAX_ADRESS];//地址
}PersonIfon;
同时由于要通讯录所以之前顺序表的Sqelist.h的数组类型也要更改,由用来的整型改变为PersonIfon,要实现该改变就需要在Seqlist.h内代码的头加上#define“contact.h”
这时Seqlist.h就变为以下形式
#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include"contact.h"
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>
typedef struct PersonIfon SLDataType;
typedef struct Seqlist
{
SLDataType* arr;
int size;//有效的数据个数
int capacity;//空间大小
}SL;//将struct Seqlist重命名为SL
在之前的顺序表的结构体struct Seqlist被重命名为SL,但现在我们要实现的是通讯录这时这个名字就不太合适与直观,那该如何来在contact.h文件内对struct Seqlist进行重命名呢?
这时你可能想到是在contact.h内加上预处理指令#define“Seqlist.h”,但这样就存在问题了,在之前的操作中我们已经在Seqlaist.h文件内包含了contact.h,如果在这再在contact.h内包含Seqlist.h就会存在头文件相互包含的问题,这时程序就会崩溃
所以正确的解决方法是什么呢?
其实这时只需要在contact.h中先声明以下struct Seqlist就可以对该结构体进行重命名了
struct Seqlist;
typedef struct Seqlist contact;
2.2 通讯录的初始化以及销毁
在以上完成联系人结构体的定义以及对顺序表内数组类型的更改,接下来就可以来实现通讯录初始化和销毁的函数了
在此先在contact.h内声明初始化函数以及销毁函数,由于在此要对通讯录内容进行更改,所以要进行传址调用,两个函数的参数都是结构体指针
void ContactInit(contact* con);//初始化通讯录 void ContactDestory(contact* con);//销毁通讯录
声明完之后就是在contact.c内实现以上两个函数,在此通讯录的初始化以及销毁内就可以直接调用之前顺序表的初始化以及销毁函数了
void ContactInit(contact* con)//通讯录初始化
{
SLInit(con);
}
void ContactDestory(contact* con)//销毁通讯录
{
SLDestory(con);
}
2.3通讯录的展示
通讯录的展示就是将通讯录的信息都打印出来,实质就是要遍历一般数组
在此在printf中在占位符中的%后加入的最小宽度限制来让打印出的通讯录更有顺序
void ContactShow(contact* con)//展示通讯录
{
printf("%-10s %-4s %-4s %15s %-20s\n", "姓名", "性别", "年龄", "电话", "地址");
for (int i = 0; i < con->size; i++)
{
printf("%-10s %-4s %-4d %15s %-20s\n",
con->arr[i].name,
con->arr[i].Gender,
con->arr[i].age,
con->arr[i].Tele,
con->arr[i].Adress);
}
}
2.4通讯录各功能实现
在以上完成了通讯录的初始化和销毁接下来我们就来实现通讯录增、删、查、改的功能了
2.4.1 通讯录内添加联系人
在此要实现通讯录中联系人的增加先在contact.h内对增加联系人函数进行声明,由于在此要对通讯录内容进行更改,所以要进行传址调用,函数的参数是结构体指针
void ContactAdd(contact* con);//通讯录内添加联系人
接下来就是在contact.c内完成添加联系人的函数
在此先创建一个结构体PersonIfon ifon,并且使用scanf来读取用户输入的此联系人的各信息存储在结构体ifon内,再调用顺序表中的插入函数将该结构体ifon插入到数组内,在此使用的是尾插函数,也可以使用其他插入方法
void ContactAdd(contact* con)//在通讯录内添加联系人
{
PersonIfon ifon;
printf("请输入联系人姓名:\n");
scanf("%s", &ifon.name);
printf("请输入联系人性别:\n");
scanf("%s", &ifon.Gender);
printf("请输入联系人年龄:\n");
scanf("%d", &ifon.age);
printf("请输入联系人电话:\n");
scanf("%s", &ifon.Tele);
printf("请输入联系人地址:\n");
scanf("%s", &ifon.Adress);
SLPushBack(con, ifon);
}
2.4.2 通讯录内删除联系人
在此要实现通讯录中联系人的删除先在contact.h内对删除联系人函数进行声明,由于在此要对通讯录内容进行更改,所以要进行传址调用,函数的参数是结构体指针
void ContactDel(contact* con);//通讯录内删除联系人
在声明完函数之后就是对该函数的实现,但在删除通讯录内的联系人要通讯录中存在要删除的联系人才能删除,所以在函数内还要先判断,但之后通讯录的其他功能可能还要用到查找联系人是否存在
所以可以直接在创建一个判断相关联系人是否存在的函数,在此根据的是名字来查找联系人
存在该联系人就返回相应的数组下标,不存在就返回-1
int FindbyName(contact* con, char* name) { for (int i = 0; i < con->size; i++) { if (strcmp(con->arr[i].name, name)==0) { return i; } } return -1; }
接下来就是在contact.c内完成删除联系人的函数
在此函数内先定义一个char类型的数组name,大小为MAX_NAME。用scanf将用户输入的联系人姓名存放在该数组内,之后再将指针con与name作为FindbyName的参数,通过FindbyName函数的返回值来得到要删除的联系人是否存在。若返回值小于0则说明该联系人不存在,之后就直接退出函数ContactDel,否则就调用顺序表中的任意位置的删除函数SLErase,这时tmp就是要删除的数组下标
void ContactDel(contact* con)//通讯录内删除联系人
{
char name[MAX_NAME];
printf("请输入要删除的联系人的姓名\n");
scanf("%s", name);
int tmp = FindbyName(con, name);
if (tmp < 0)
{
printf("该联系人不存在,删除失败\n");
return;
}
SLErase(con, tmp);
printf("删除成功\n");
}
2.4.3 通讯录内修改联系人
在此要实现通讯录中联系人的修改先在contact.h内对修改联系人函数进行声明,由于在此要对通讯录内容进行更改,所以要进行传址调用,函数的参数是结构体指针
void ContactModify(contact* con);//通讯录内修改联系人
接下来就是在contact.c内完成修改联系人的函数
在该函数内和删除联系人函数一样也是先在此函数内先定义一个char类型的数组name,大小为MAX_NAME。用scanf将用户输入的联系人姓名存放在该数组内,之后再将指针con与name作为FindbyName的参数,通过FindbyName函数的返回值来得到要修改的联系人是否存在。若返回值小于0则说明该联系人不存在,之后就直接退出函数ContactDel。否则就使用scanf将用户输入的信息存放到原来联系人的数组位置
void ContactModify(contact* con)//修改联系人信息
{
char name[MAX_NAME];
printf("请输入要修改的联系人的姓名\n");
scanf("%s", name);
int tmp = FindbyName(con, name);
if (tmp < 0)
{
printf("该联系人不存在,修改失败\n");
return;
}
//直接修改
printf("请输入新的联系人姓名:\n");
scanf("%s", con->arr[tmp].name);
printf("请输入新的联系人性别:\n");
scanf("%s", con->arr[tmp].Gender);
printf("请输入新的联系人年龄:\n");
scanf("%d", &con->arr[tmp].age);
printf("请输入新的联系人电话:\n");
scanf("%s", con->arr[tmp].Tele);
printf("请输入新的联系人地址:\n");
scanf("%s", con->arr[tmp].Adress);
printf("修改成功!\n");
}
2.4.4 通讯录内查找联系人
在此要实现通讯录中联系人的修改先在contact.h内对修改联系人函数进行声明,在此虽然查找联系人未对通讯录内数据进行修改,但在此还是将结构体作为函数的参数,原因通讯录其他函数都是传地址在此也保持一致性
void ContactFind(contact* con);//在通讯录内查找联系人
接下来就是在contact.c内完成查找联系人的函数
在该函数内也是先定义一个char类型的数组name,再通过scanf函数将用户输入的字符串存储到数组内,在此并定义一个变量flag=-1。再通过for循环遍历数组中的各个元素中的name是否和数组name相同,相同就将flag赋值为1,相同就打印该元素的联系人信息,当遍历完时flag如果等于-1就打印查找不到联系人
void ContactFind(contact* con)//查找联系人
{
char name[MAX_NAME];
printf("请输入要查找的联系人的姓名\n");
scanf("%s", name);
int flag = -1;
for (int i = 0; i < con->size; i++)
{
if (strcmp(con->arr[i].name, name) == 0)
{
flag = 1;
printf("查找成功!\n");
printf("%-10s %-4s %-4s %15s %-20s\n", "姓名", "性别", "年龄", "电话", "地址");
printf("%-10s %-4s %-4d %15s %-20s\n",
con->arr[i].name,
con->arr[i].Gender,
con->arr[i].age,
con->arr[i].Tele,
con->arr[i].Adress);
}
}
if (flag == -1)
{
printf("你所要查找的联系人不存在!\n");
}
}
3.通讯录test.c文件
在以上我们已经实现了通讯录各种功能,现在就需要在test.c内将这些设计好的功能给拼装起来,让程序能在运行窗口通过输入不同的数就可以实现通讯录的各功能
注:在test.c的开头要加上#include"SeqList.h"和#include"contact.h"
先在test.c内先创建main函数,同时要实现对通讯录的多次操作就需要用到循环,在此用的是do...while循环,在循环内在创建一个switch语句来实现用户输入不同的信息就进入不同的函功能。在switch语句内的case后的常量都使用枚举所定义的,这样会使得代码的可读性更高、同时shitch语句的case语句排序也可以是不按顺序来的。在不同的case语句内就调用不同的通讯录功能函数
在进入循环先初始化顺序表,退出循环就销毁顺序表
#include"SeqList.h"
#include"contact.h"
void menu()
{
printf("**************************************\n");
printf("******1.添加联系人 2.删除联系人******\n");
printf("******3.查找联系人 4.修改联系人******\n");
printf("******5.展示通讯录 0.退出程序 ******\n");
printf("**************************************\n");
}
enum option
{
ADD=1,
DEL=2,
FIND=3,
MODIFY=4,
SHOW=5
};
int main()
{
int input = 0;
contact con;
ContactInit(&con);
do
{
menu();
printf("请选择操作\n");
scanf("%d", &input);
switch (input)
{
case ADD:
ContactAdd(&con);
break;
case DEL:
ContactDel(&con);
break;
case FIND:
ContactFind(&con);
break;
case MODIFY:
ContactModify(&con);
break;
case SHOW:
ContactShow(&con);
break;
default:
printf("选择操作错误,请输入1~5内的数\n");
break;
}
} while(input);//当输入的值为0时,input值也就也0,在此判断部分input就为假就会退出循环
ContactDestory(&con);
return 0;
}
4. 通讯录读取历史数据和保存数据
在之前已经实现了通讯录的各功能的数据只能在程序运行窗口打开的时候进行通讯录的增、删、查、改等功能。在关闭窗口后对通讯录进行的各项操作都不会保存,那么要怎么样才能让我们设计的通讯录在每次开始之前都读取之前的信息,在结束后都保存通讯录的信息呢?
在之前文件操作章节中讲解了如何将程序数据输出到文件中,将文件数据输入到程序当中,所以在通讯录中我们就可以用到文件操作的相关函数来实现通讯录数据的保存与读取。
4.1读取历史数据
要读取历史数据就要在通讯录每次初始化之后就输入文件的信息到所创建的通讯录中,也就是将输入到数组当中。
在此先在我们创建的程序的文件夹中创建一个con.txt文本,再使用fopen以读的方式打开文件,再创建一个PersonIfon ifon的变量,之后在使用到fread以二进制的形式输入文件的信息到创建的ifon中,再将ifon尾插到数组当中。
void ContactRead(contact* con)//从文件中读取历史数据
{
FILE* pf = fopen("con.txt", "rb");
if (pf == NULL)
{
perror("fopen");
return;
}
PersonIfon ifon;
while (fread(&ifon, sizeof(PersonIfon), 1, pf))
{
SLPushBack(con, ifon);
}
printf("成功读取历史数据到通讯录中\n");
}
在通讯录初始化函数中调用SLInit函数后调用COntactRead就可以实现历史数据的读取
void ContactInit(contact* con)//通讯录初始化 { SLInit(con); ContactRead(con); }
4.2 保存数据
要实现通讯录每次在退出程序后都能将数据保留,这就需要在每次销毁通讯录前将通讯录内的数据,也就是数组的所有元素都输出到con.txt文件内。
要把数组的所有元素都输出到con.txt文件内就需要先以写的方式打开文件,后在循环的使用fwrite将数组的数据以二进制的形式输出到文件当中
void ContactWrite(contact* con)//将通讯录数据读入文件中
{
FILE* pf = fopen("con.txt", "wb");
if (pf == NULL)
{
perror("fopen\n");
return;
}
for (int i = 0; i < con->size; i++)
{
fwrite(con->arr+i, sizeof(PersonIfon), 1, pf);
}
printf("通讯录数据保存成功\n");
}
在通讯录销毁函数中调用SLDestory函数前调用COntactWrite就可以实现数据输出到文件当中,也就将数据保留了下来
void ContactDestory(contact* con)//销毁通讯录 { ContactWrite(con); SLDestory(con); }
通讯录完整代码
注:在Seqlist.h和Seqlist.c内的查找和打印顺序表与以上代码不兼容,运行时会使得程序崩溃,所以将这些部分注释掉
contact.h
#pragma once
#define MAX_NAME 20
#define MAX_GENDER 10
#define MAX_TELE 20
#define MAX_ADRESS 50
typedef struct PersonIfon//联系人信息
{
char name[MAX_NAME];//姓名
char Gender[MAX_GENDER];//性别
int age;//年龄
char Tele[MAX_TELE];//电话
char Adress[MAX_ADRESS];//地址
}PersonIfon;
struct Seqlist;
typedef struct Seqlist contact;
void ContactInit(contact* con);//初始化通讯录
void ContactDestory(contact* con);//销毁通讯录
void ContactAdd(contact* con);//通讯录内添加联系人
void ContactDel(contact* con);//通讯录内删除联系人
void ContactModify(contact* con);//通讯录内修改联系人
void ContactFind(contact* con);//在通讯录内查找联系人
void ContactShow(contact* con);//展示通讯录
void ContactRead(contact* con);//从文件中读取历史数据
void ContactWrite(contact* con);//将通讯录数据读入文件中
Seqlist.h
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>
typedef struct PersonIfon SLDataType;
typedef struct Seqlist
{
SLDataType* arr;
int size;//有效的数据个数
int capacity;//空间大小
}SL;//将struct Seqlist重命名为SL
void SLInit(SL* ps);//初始化
void SLDestory(SL* ps);//销毁
void SLCheckCapacity(SL* ps);//检查空间是否足够
//void SLPrint(SL ps);//打印顺序表
void SLPushBack(SL* ps, SLDataType x);//尾插
void SLPushPront(SL* ps, SLDataType x);//头插
void SLPopBack(SL* ps);//尾删
void SLPopPront(SL* ps);//头删
void SLInsert(SL* ps, int pos, SLDataType x);//任意位置插入
void SLErase(SL* ps, int pos);//任意位置删除
//int SLFind(SL* ps, SLDataType x);//查找
Seqlist.c
#include"SeqList.h"
void SLInit(SL* ps)//顺序表初始化
{
ps->arr = NULL;
ps->size = ps->capacity = 0;
}
void SLDestory(SL* ps)//顺序表销毁
{
if (ps->arr)
{
free(ps->arr);
}
ps->arr = NULL;
ps->size = ps->capacity = 0;
}
void SLCheckCapacity(SL* ps)//检查空间是否足够,不足时增大空间大小
{
if (ps->size == ps->capacity)
{
int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
SLDataType* tmp = (SLDataType*)realloc(ps->arr, newcapacity * sizeof(SLDataType));
if (tmp == NULL)
{
perror("realloc");
exit(1);
}
ps->arr = tmp;
ps->capacity = newcapacity;
}
}
//void SLPrint(SL ps)//打印
//{
// int i = 0;
// for (i; i < ps.size; i++)
// {
// printf("%d ", ps.arr[i]);
// }
// printf("\n");
//}
void SLPushBack(SL* ps, SLDataType x)//尾插
{
assert(ps);
SLCheckCapacity(ps);
ps->arr[ps->size++] = x;
}
void SLPushPront(SL* ps, SLDataType x)//头插
{
assert(ps);
SLCheckCapacity(ps);
for (int i = ps->size; i > 0; i--)
{
ps->arr[i] = ps->arr[i - 1]; //pa->arr[1]=pa->arr[0]
}
ps->arr[0] = x;
ps->size++;
}
void SLPopBack(SL* ps)//尾删
{
assert(ps);
assert(ps->size);
ps->size--;
}
void SLPopPront(SL* ps)//头删
{
assert(ps);
assert(ps->size);
for (int i = 0; i < ps->size - 1; i++)
{
ps->arr[i] = ps->arr[i + 1];
}
ps->size--;
}
void SLInsert(SL* ps, int pos, SLDataType x)//任意位置插入
{
assert(ps);
assert(pos >= 0 && pos <= ps->size);
SLCheckCapacity(ps);
for (int i = ps->size; i > pos; i--)
{
ps->arr[i] = ps->arr[i - 1];//结束条件ps->arr[pos+1] = ps->arr[pos]
}
ps->arr[pos] = x;
ps->size++;
}
void SLErase(SL* ps, int pos)//任意位置删除
{
assert(ps);
assert(pos >= 0 && pos < ps->size);
for (int i = pos; i <= ps->size - 2; i++)
{
ps->arr[i] = ps->arr[i + 1];//结束条件ps->arr[ps->size-2] = ps->arr[ps->size-1]
}
--ps->size;
}
//int SLFind(SL* ps, SLDataType x)//查找
//{
// assert(ps);
// for (int i = 0; i < ps->size; i++)
// {
// if (ps->arr[i] == x)
// {
// return i;
// }
// }
// return -1;
//
//}
contact.c
#pragma once
#include "contact.h"
#include"SeqList.h"
void ContactRead(contact* con)//从文件中读取历史数据
{
FILE* pf = fopen("con.txt", "rb");
if (pf == NULL)
{
perror("fopen");
return;
}
PersonIfon ifon;
while (fread(&ifon, sizeof(PersonIfon), 1, pf))
{
SLPushBack(con, ifon);
}
printf("成功读取历史数据到通讯录中\n");
}
void ContactInit(contact* con)//通讯录初始化
{
SLInit(con);
ContactRead(con);
}
void ContactWrite(contact* con)//将通讯录数据读入文件中
{
FILE* pf = fopen("con.txt", "wb");
if (pf == NULL)
{
perror("fopen\n");
return;
}
for (int i = 0; i < con->size; i++)
{
fwrite(con->arr+i, sizeof(PersonIfon), 1, pf);
}
printf("通讯录数据保存成功\n");
}
void ContactDestory(contact* con)//销毁通讯录
{
ContactWrite(con);
SLDestory(con);
}
void ContactAdd(contact* con)//在通讯录内添加联系人
{
PersonIfon ifon;
printf("请输入联系人姓名:\n");
scanf("%s", &ifon.name);
printf("请输入联系人性别:\n");
scanf("%s", &ifon.Gender);
printf("请输入联系人年龄:\n");
scanf("%d", &ifon.age);
printf("请输入联系人电话:\n");
scanf("%s", &ifon.Tele);
printf("请输入联系人地址:\n");
scanf("%s", &ifon.Adress);
SLPushBack(con, ifon);
}
int FindbyName(contact* con, char* name)
{
for (int i = 0; i < con->size; i++)
{
if (strcmp(con->arr[i].name, name)==0)
{
return i;
}
}
return -1;
}
void ContactDel(contact* con)//通讯录内删除联系人
{
char name[MAX_NAME];
printf("请输入要删除的联系人的姓名\n");
scanf("%s", name);
int tmp = FindbyName(con, name);
if (tmp < 0)
{
printf("该联系人不存在,删除失败\n");
return;
}
SLErase(con, tmp);
printf("删除成功\n");
}
void ContactModify(contact* con)//修改联系人信息
{
char name[MAX_NAME];
printf("请输入要修改的联系人的姓名\n");
scanf("%s", name);
int tmp = FindbyName(con, name);
if (tmp < 0)
{
printf("该联系人不存在,修改失败\n");
return;
}
printf("请输入新的联系人姓名:\n");
scanf("%s", con->arr[tmp].name);
printf("请输入新的联系人性别:\n");
scanf("%s", con->arr[tmp].Gender);
printf("请输入新的联系人年龄:\n");
scanf("%d", &con->arr[tmp].age);
printf("请输入新的联系人电话:\n");
scanf("%s", con->arr[tmp].Tele);
printf("请输入新的联系人地址:\n");
scanf("%s", con->arr[tmp].Adress);
printf("修改成功!\n");
}
void ContactFind(contact* con)//查找联系人
{
char name[MAX_NAME];
printf("请输入要查找的联系人的姓名\n");
scanf("%s", name);
int flag = -1;
for (int i = 0; i < con->size; i++)
{
if (strcmp(con->arr[i].name, name) == 0)
{
flag = 1;
printf("查找成功!\n");
printf("%-10s %-4s %-4s %15s %-20s\n", "姓名", "性别", "年龄", "电话", "地址");
printf("%-10s %-4s %-4d %15s %-20s\n",
con->arr[i].name,
con->arr[i].Gender,
con->arr[i].age,
con->arr[i].Tele,
con->arr[i].Adress);
}
}
if (flag == -1)
{
printf("你所要查找的联系人不存在!\n");
}
}
void ContactShow(contact* con)//展示通讯录
{
printf("%-10s %-4s %-4s %15s %-20s\n", "姓名", "性别", "年龄", "电话", "地址");
for (int i = 0; i < con->size; i++)
{
printf("%-10s %-4s %-4d %15s %-20s\n",
con->arr[i].name,
con->arr[i].Gender,
con->arr[i].age,
con->arr[i].Tele,
con->arr[i].Adress);
}
}
test.c
#include"SeqList.h"
#include"contact.h"
void menu()
{
printf("**************************************\n");
printf("******1.添加联系人 2.删除联系人******\n");
printf("******3.查找联系人 4.修改联系人******\n");
printf("******5.展示通讯录 0.退出程序 ******\n");
printf("**************************************\n");
}
enum option
{
ADD=1,
DEL=2,
FIND=3,
MODIFY=4,
SHOW=5
};
int main()
{
int input = 0;
contact con;
ContactInit(&con);
do
{
menu();
printf("请选择操作\n");
scanf("%d", &input);
switch (input)
{
case ADD:
ContactAdd(&con);
break;
case DEL:
ContactDel(&con);
break;
case FIND:
ContactFind(&con);
break;
case MODIFY:
ContactModify(&con);
break;
case SHOW:
ContactShow(&con);
break;
default:
printf("选择操作错误,请输入1~5内的数\n");
break;
}
} while(input);
ContactDestory(&con);
return 0;
}
通讯录实现效果
基于顺序表实现通讯录
以上就是本篇的所有内容了,希望能得到你的点赞与收藏,感谢支持!!!