文章目录
前言
实现通讯录,先要想好你的通讯录需要哪些功能。我这里实现了8个功能
- 新建联系人
- 打印联系人
- 删除联系人
- 修改联系人
- 查找联系人
- 排序联系人
- 查找联系人
- 保存通讯录
- 退出通讯录
- 初始化通讯录
- 自动增容
- 打开已保存的联系人信息
当然这是一个可以以文件方式储存的通讯录,还可以自动增容。所以还有自动增容功能,和打开文件功能。
我这里分了三个模块来写代码
第一个模块写函数的的声明
第二个模块写函数的实现
第三个模块写的是主函数
提示:以下是本篇文章正文内容,下面案例可供参考
一、主函数
void menu()
{
printf("*****************************************\n");
printf("******* 1.新建联系人 2.打印联系人 *******\n");
printf("******* 3.删除联系人 4.修改联系人 *******\n");
printf("******* 5.查找联系人 6.排序联系人 *******\n");
printf("******* 7.保存通讯录 0.退出通讯录 *******\n");
printf("*****************************************\n");
}
enum Menus
{
EXIT,
ADD,
SHOW,
DELETE,
AMEND,
FIND,
RANGK,
SAVE
};
void test()
{
int input = 0;
struct contact con;
Initialize(&con);//初始化通讯录
do
{
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case ADD:
AddContact(&con);
break;
case SHOW:
ShowContact(&con);
break;
case DELETE:
DeleteContact(&con);
break;
case AMEND:
AmendContact(&con);
break;
case FIND:
FindContact(&con);
break;
case RANGK:
RangkContact(&con);
break;
case SAVE:
Conserve(&con);
break;
case EXIT:
DisposalContact(&con);
printf("通讯录已经退出\n");
break;
default:
printf("输入错误请重新输入\n");
break;
}
} while (input);
}
int main()
{
test();
return 0;
}
这里我创建了一个枚举类型,增加了代码的可读性。因为枚举是从0开始的,刚好对应了菜单的选项。接着就是do,while循环嵌套switch来来选择,只有当输入0也就是才会结束循环退出程序,而0正好对应的是枚举的第一个成员EXIT。
二、函数的声明
这里我用#define定义了 名字 年龄 电话 和住址数组的最大长度,还有通讯录最大长度方便修改
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#define NAME_MAX 10
#define SEX_MAX 4
#define PHONE_MAX 12
#define ADDRESS_MAX 30
#define MAX 3//通讯录最大容量
//存放联系人信息的结构体
typedef struct information
{
char name[NAME_MAX];
char sex[SEX_MAX];
int age;
char phone[PHONE_MAX];
char address[ADDRESS_MAX];
}information;
//存放联系人的结构体
typedef struct contact
{
struct information *data;
int people;//记录联系人的个数
int cont;//通讯录最大的容量
}contact;//重命名结构体
//初始化通讯录
void Initialize(struct contact* pc);
//新建联系人
void AddContact(struct contact* pc);
//打印联系人
void ShowContact(const struct contact* pc);
//删除联系人
void DeleteContact(struct contact* pc);
//修改联系人
void AmendContact(struct contact* pc);
//查找联系人
void FindContact(const struct contact* pc);
//排序联系人
void RangkContact(struct contact* pc);
//销毁通讯录
void DisposalContact(struct contact* pc);
//保存通讯录数据到文件
void Conserve(contact* pc);
二、函数的实现
1.判断通讯录是否要增容
首先已进入通讯录肯定要判断通讯录是否要增容,如果空间不够肯定扩容通讯录。当联系人的个数people等于通讯录的最大个数cont时,通过realloc函数扩容通讯录,这里的+2是每次扩容两个联系人的空间
//判断通讯录是否要增容
void dilatation(contact* pc)
{
if (pc->people == pc->cont)
{
//每次扩容两个联系人空间
struct information* ptr = (struct information*)realloc(pc->data, (pc->cont + 2) * sizeof(struct information));
if (ptr == NULL)
{
printf("通讯录扩容失败\n");
return;
}
else
{
pc->data = ptr;
pc->cont += 2;//扩容增加两个联系人空间
printf("通讯录扩容成功\n");
}
}
}
2.打开已保存的联系人文件
这里用到了文件操作函数,rb是打开一个二进制文件,因为我们待会储存的联系人信息是以二进制的方式存储的,因为二进制文件可以保护联系人的信息,直接看是看不懂的,详细的函数介绍可以看。
C语言文件操作(fopen,fclose)
void ReadContact(contact* pc)
{
//打开文件
FILE* pfIn = fopen("contact.dat", "rb");
if (pfIn == NULL)
{
printf("ReadContact::%s\n", strerror(errno));//打开文件失败时报错
return;
}
//把联系人信息导入通讯录
information tmp = { 0 };
while (fread(&tmp, sizeof(information), 1, pfIn))//fread返回的是读取文件信息的个数,当没有读取到时候返回0
{
//判断通讯录是否要增容
dilatation(pc);
//导入信息
pc->data[pc->people] = tmp;
pc->people++;
}
//关闭文件
fclose(pfIn);
pfIn = NULL;
}
3.初始化通讯录
通讯录一开始是没有联系人的,所以要初始化通讯录。初始化的同时为通讯录开辟内存,最多可以存储MAX个联系人,这个MAX是已经用#define定义好的一个宏。动态内存开辟函数可以看看这一篇博客
动态内存管理
//动态版初始化通讯录
void Initialize(struct contact* pc)
{
pc->people = 0;//通讯录创建好的时候没有有效的信息
pc->cont = MAX;//一开始通讯录只能放MAX个人的信息
pc->data = (struct information*)malloc(MAX * sizeof(struct information));
if (malloc == NULL)
{
printf("初始化通讯录失败\n");
exit(1);//失败退出整个代码
}
//从文件读取联系人信息放到通讯录
ReadContact(pc);
}
4.新增联系人
新增联系人之前调用前面第一个我们已经说了的函数
**dilatation()**来判断通讯录是否要扩容
//动态新增联系人
void AddContact(struct contact* pc)
{
dilatation(pc);//判断通讯录是否要增容
printf("请输入添加联系人的姓名:>");
scanf("%s", pc->data[pc->people].name);
printf("性别:>");
scanf("%s", pc->data[pc->people].sex);
printf("年龄:>");
scanf("%d", &(pc->data[pc->people].age));
printf("电话:>");
scanf("%s", pc->data[pc->people].phone);
printf("住址:>");
scanf("%s", pc->data[pc->people].address);
printf("成功添加联系人\n");
pc->people++;
}
5.打印联系人
void ShowContact(const struct contact* pc)
{
if (pc->people == 0)
{
printf("通讯录为空\n");
}
else
{
int i = 0;
printf("%-10s\t%-10s\t%-10s\t%-10s\t%-10s\n", "姓名", "性别", "年龄", "电话", "住址");
for (i = 0; i < pc->people; i++)
{
printf("%-10s\t%-10s\t%-10d\t%-10s\t%-10s\n",
pc->data[i].name,
pc->data[i].sex,
pc->data[i].age,
pc->data[i].phone,
pc->data[i].address);
}
}
}
6.查找联系人
这是一个查找联系人的函数,为什么单独写一个这样的函数呢?因为下面的 删除联系人、修改联系人以及查找联系人都要用到这个功能,所以我把查找功能封装成了一个函数。
//这是个查找联系人的函数
static LookupContact(const struct contact* pc, char* name)
{
int i = 0;
for (i = 0; i < pc->people; i++)
{
if (strcmp(pc->data[i].name, name) == 0)
{
return i;
}
}
//通讯录里没有要找的联系人时
return -1;
}
7.删除联系人
删除联系人之前直接调用我们之前写的函数LookupContact来判断通讯录里有没有该联系人
void DeleteContact(struct contact* pc)
{
if (pc->people == 0)
{
printf("通讯录为空\n");
}
else
{
char name[NAME_MAX];
printf("请输入要删除联系人的姓名:>");
scanf("%s", name);
int ret = LookupContact(pc, name);
if (ret == -1)
{
printf("删除失败,通讯录没有该联系人\n");
}
else
{
int j = 0;
for (j = ret; j < pc->people; j++)
{
pc->data[j] = pc->data[j + 1];
}
printf("删除成功\n");
pc->people--;
}
}
}
8.修改联系人
void AmendContact(struct contact* pc)
{
if (pc->people == 0)
{
printf("通讯录为空\n");
}
else
{
char name[NAME_MAX];
printf("请输入要修改人的姓名");
scanf("%s", name);
int ret = LookupContact(pc, name);
if (ret == -1)
{
printf("通讯录中没有该联系人\n");
}
else
{
printf("请输入要修改联系人的姓名:>");
scanf("%s", pc->data[ret].name);
printf("性别:>");
scanf("%s", pc->data[ret].sex);
printf("年龄:>");
scanf("%d", &(pc->data[ret].age));
printf("电话:>");
scanf("%s", pc->data[ret].phone);
printf("住址:>");
scanf("%s", pc->data[ret].address);
printf("修改成功\n");
}
}
}
9.查找一个联系人并打印
void FindContact(const struct contact* pc)
{
if (pc->people == 0)
{
printf("查找失败,通讯录为空\n");
}
else
{
char name[NAME_MAX];
printf("请输入要查找人的姓名:>");
scanf("%s", name);
int ret = LookupContact(pc, name);
if (ret == -1)
{
printf("通讯录中没有该联系人\n");
}
else
{
printf("%-10s\t%-10s\t%-10s\t%-10s\t%-10s\n", "姓名", "性别", "年龄", "电话", "住址");
printf("%-10s\t%-10s\t%-10d\t%-10s\t%-10s\n",
pc->data[ret].name,
pc->data[ret].sex,
pc->data[ret].age,
pc->data[ret].phone,
pc->data[ret].address);
}
}
}
9.排序联系人
这里我用了qsort函数实现了两种排序方式qsort快速排序函数详解
//按名字排序
static int name_sort(const void* s1, const void* s2)
{
return strcmp(((struct information*)s1)->name,((struct information*)s2)->name);
}
//按年龄排序
static int age_sort(const void* s1, const void* s2)
{
return (((struct information*)s1)->age) - (((struct information*)s2)->age);
}
void RangkContact(struct contact* pc)
{
int x = 1;
while (x)//只有排序成功才会停止循环
{
printf("请选择排序方式\n1/0(名字/年龄):>");
scanf("%d", &x);
switch (x)
{
case 1:
qsort(pc->data, pc->people, sizeof(pc->data[0]), name_sort);
printf("按名字排序联系人成功\n");
x = 0;
break;
case 0:
qsort(pc->data, pc->people, sizeof(pc->data[0]), age_sort);
printf("按年龄排序联系人成功\n");
}
}
}
10.退出通讯录
free函数释放开辟的内存
文件关闭后,把文件信息区地址的指针置为NULL,避免程序出现问题
void DisposalContact(struct contact* pc)
{
free(pc->data);
pc->data = NULL;
pc->people = 0;
pc->cont = 0;
}
11.保存通讯录数据到文件
wb是以二进制的形式把联系人信息写入文件保存,同时上面的打开文件方式也要对应起来。如果没有 contact.dat 文件则么会自动新建一个contact.dat二进制文件。
void Conserve(contact* pc)
{
//打开文件
FILE* pfOut = fopen("contact.dat", "wb");//以二进制的方式打开
if (pfOut == NULL)
{
printf("Conserve::%s", strerror(errno));//打开文件失败报错退出
return;
}
//写数据
int i = 0;
for (i = 0; i < pc->people; i++)
{
fwrite(pc->data + i, sizeof(information), 1,pfOut);//以二进制的方式写入信息
}
//关闭文件
fclose(pfOut);
pfOut = NULL;
printf("联系人信息保存成功\n");
}
三、所有代码
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#define NAME_MAX 10
#define SEX_MAX 4
#define PHONE_MAX 12
#define ADDRESS_MAX 30
#define MAX 3//通讯录最大容量
//存放联系人信息的结构体
typedef struct information
{
char name[NAME_MAX];
char sex[SEX_MAX];
int age;
char phone[PHONE_MAX];
char address[ADDRESS_MAX];
}information;
//存放联系人的结构体
typedef struct contact
{
struct information *data;
int people;//记录联系人的个数
int cont;//通讯录最大的容量
}contact;//重命名结构体
//初始化通讯录
void Initialize(struct contact* pc);
//新建联系人
void AddContact(struct contact* pc);
//打印联系人
void ShowContact(const struct contact* pc);
//删除联系人
void DeleteContact(struct contact* pc);
//修改联系人
void AmendContact(struct contact* pc);
//查找联系人
void FindContact(const struct contact* pc);
//排序联系人
void RangkContact(struct contact* pc);
//销毁通讯录
void DisposalContact(struct contact* pc);
//保存通讯录数据到文件
void Conserve(contact* pc);
//判断通讯录是否要增容
void dilatation(contact* pc)
{
if (pc->people == pc->cont)
{
//每次扩容两个联系人空间
struct information* ptr = (struct information*)realloc(pc->data, (pc->cont + 2) * sizeof(struct information));
if (ptr == NULL)
{
printf("通讯录扩容失败\n");
return;
}
else
{
pc->data = ptr;
pc->cont += 2;//扩容增加两个联系人空间
printf("通讯录扩容成功\n");
}
}
}
void ReadContact(contact* pc)
{
//打开文件
FILE* pfIn = fopen("contact.dat", "rb");
if (pfIn == NULL)
{
printf("ReadContact::%s\n", strerror(errno));//打开文件失败时报错
return;
}
//把联系人信息导入通讯录
information tmp = { 0 };
while (fread(&tmp, sizeof(information), 1, pfIn))//fread返回的是读取文件信息的个数,当没有读取到时候返回0
{
//判断通讯录是否要增容
dilatation(pc);
//导入信息
pc->data[pc->people] = tmp;
pc->people++;
}
//关闭文件
fclose(pfIn);
pfIn = NULL;
}
//动态版初始化通讯录
void Initialize(struct contact* pc)
{
pc->people = 0;//通讯录创建好的时候没有有效的信息
pc->cont = MAX;//一开始通讯录只能放MAX个人的信息
pc->data = (struct information*)malloc(MAX * sizeof(struct information));
if (malloc == NULL)
{
printf("初始化通讯录失败\n");
exit(1);//失败退出整个代码
}
//从文件读取联系人信息放到通讯录
ReadContact(pc);
}
//动态新增联系人
void AddContact(struct contact* pc)
{
dilatation(pc);//判断通讯录是否要增容
printf("请输入添加联系人的姓名:>");
scanf("%s", pc->data[pc->people].name);
printf("性别:>");
scanf("%s", pc->data[pc->people].sex);
printf("年龄:>");
scanf("%d", &(pc->data[pc->people].age));
printf("电话:>");
scanf("%s", pc->data[pc->people].phone);
printf("住址:>");
scanf("%s", pc->data[pc->people].address);
printf("成功添加联系人\n");
pc->people++;
}
void ShowContact(const struct contact* pc)
{
if (pc->people == 0)
{
printf("通讯录为空\n");
}
else
{
int i = 0;
printf("%-10s\t%-10s\t%-10s\t%-10s\t%-10s\n", "姓名", "性别", "年龄", "电话", "住址");
for (i = 0; i < pc->people; i++)
{
printf("%-10s\t%-10s\t%-10d\t%-10s\t%-10s\n",
pc->data[i].name,
pc->data[i].sex,
pc->data[i].age,
pc->data[i].phone,
pc->data[i].address);
}
}
}
//这是个查找联系人的函数
static LookupContact(const struct contact* pc, char* name)
{
int i = 0;
for (i = 0; i < pc->people; i++)
{
if (strcmp(pc->data[i].name, name) == 0)
{
return i;
}
}
//通讯录里没有要找的联系人时
return -1;
}
void DeleteContact(struct contact* pc)
{
if (pc->people == 0)
{
printf("通讯录为空\n");
}
else
{
char name[NAME_MAX];
printf("请输入要删除联系人的姓名:>");
scanf("%s", name);
int ret = LookupContact(pc, name);
if (ret == -1)
{
printf("删除失败,通讯录没有该联系人\n");
}
else
{
int j = 0;
for (j = ret; j < pc->people; j++)
{
pc->data[j] = pc->data[j + 1];
}
printf("删除成功\n");
pc->people--;
}
}
}
void AmendContact(struct contact* pc)
{
if (pc->people == 0)
{
printf("通讯录为空\n");
}
else
{
char name[NAME_MAX];
printf("请输入要修改人的姓名");
scanf("%s", name);
int ret = LookupContact(pc, name);
if (ret == -1)
{
printf("通讯录中没有该联系人\n");
}
else
{
printf("请输入要修改联系人的姓名:>");
scanf("%s", pc->data[ret].name);
printf("性别:>");
scanf("%s", pc->data[ret].sex);
printf("年龄:>");
scanf("%d", &(pc->data[ret].age));
printf("电话:>");
scanf("%s", pc->data[ret].phone);
printf("住址:>");
scanf("%s", pc->data[ret].address);
printf("修改成功\n");
}
}
}
void FindContact(const struct contact* pc)
{
if (pc->people == 0)
{
printf("查找失败,通讯录为空\n");
}
else
{
char name[NAME_MAX];
printf("请输入要查找人的姓名:>");
scanf("%s", name);
int ret = LookupContact(pc, name);
if (ret == -1)
{
printf("通讯录中没有该联系人\n");
}
else
{
printf("%-10s\t%-10s\t%-10s\t%-10s\t%-10s\n", "姓名", "性别", "年龄", "电话", "住址");
printf("%-10s\t%-10s\t%-10d\t%-10s\t%-10s\n",
pc->data[ret].name,
pc->data[ret].sex,
pc->data[ret].age,
pc->data[ret].phone,
pc->data[ret].address);
}
}
}
//按名字排序
static int name_sort(const void* s1, const void* s2)
{
return strcmp(((struct information*)s1)->name,((struct information*)s2)->name);
}
//按年龄排序
static int age_sort(const void* s1, const void* s2)
{
return (((struct information*)s1)->age) - (((struct information*)s2)->age);
}
void RangkContact(struct contact* pc)
{
int x = 1;
while (x)//只有排序成功才会停止循环
{
printf("请选择排序方式\n1/0(名字/年龄):>");
scanf("%d", &x);
switch (x)
{
case 1:
qsort(pc->data, pc->people, sizeof(pc->data[0]), name_sort);
printf("按名字排序联系人成功\n");
x = 0;
break;
case 0:
qsort(pc->data, pc->people, sizeof(pc->data[0]), age_sort);
printf("按年龄排序联系人成功\n");
}
}
}
void DisposalContact(struct contact* pc)
{
free(pc->data);
pc->data = NULL;
pc->people = 0;
pc->cont = 0;
}
void Conserve(contact* pc)
{
//打开文件
FILE* pfOut = fopen("contact.dat", "wb");//以二进制的方式打开
if (pfOut == NULL)
{
printf("Conserve::%s", strerror(errno));//打开文件失败报错退出
return;
}
//写数据
int i = 0;
for (i = 0; i < pc->people; i++)
{
fwrite(pc->data + i, sizeof(information), 1,pfOut);//以二进制的方式写入信息
}
//关闭文件
fclose(pfOut);
pfOut = NULL;
printf("联系人信息保存成功\n");
}
void menu()
{
printf("*****************************************\n");
printf("******* 1.新建联系人 2.打印联系人 *******\n");
printf("******* 3.删除联系人 4.修改联系人 *******\n");
printf("******* 5.查找联系人 6.排序联系人 *******\n");
printf("******* 7.保存通讯录 0.退出通讯录 *******\n");
printf("*****************************************\n");
}
enum Menus
{
EXIT,
ADD,
SHOW,
DELETE,
AMEND,
FIND,
RANGK,
SAVE
};
void test()
{
int input = 0;
struct contact con;
Initialize(&con);//初始化通讯录
do
{
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case ADD:
AddContact(&con);
break;
case SHOW:
ShowContact(&con);
break;
case DELETE:
DeleteContact(&con);
break;
case AMEND:
AmendContact(&con);
break;
case FIND:
FindContact(&con);
break;
case RANGK:
RangkContact(&con);
break;
case SAVE:
Conserve(&con);
break;
case EXIT:
DisposalContact(&con);
printf("通讯录已经退出\n");
break;
default:
printf("输入错误请重新输入\n");
break;
}
} while (input);
}
int main()
{
test();
return 0;
}
总结
这就是一个可以储存通讯录小项目,这里的储存方式是二进制。保存之后会在你的代码文件路径自动生成一个二进制文件,当然二进制文件是看不懂的。你可以用文本文件的方式存储,
注意:修改的同时打开文件的方式也要修改