C语言实现可储存的通讯录项目


前言

实现通讯录,先要想好你的通讯录需要哪些功能。我这里实现了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;
}

总结

这就是一个可以储存通讯录小项目,这里的储存方式是二进制。保存之后会在你的代码文件路径自动生成一个二进制文件,当然二进制文件是看不懂的。你可以用文本文件的方式存储,
注意:修改的同时打开文件的方式也要修改

  • 5
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱敲代码的三毛

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值