C语言实现通讯录,可保存到文件中,小项目适合初学者。

前言

C语言实现的小通讯录,可以文件保存,适合初学者拿来练手。

目录

1.功能及需求

 2.头文件&结构体&函数声明

3.主函数&通讯录界面

4.初始化通讯录&加载已有通讯录

5.通过名字查找联系人

6.添加联系人

7.删除联系人

8.查找联系人并将他打印出来

9.修改联系人信息

10.展示通讯录

11.排序通讯录

12.保存通讯录到文件中

13.销毁通讯录(释放内存)

14.!!!!!全部代码!!!!


1.功能及需求

首先这是个通讯录,我们需要一个菜单来展示通讯录功能和选择我们想要的功能,其次,一个通讯录要可以添加、修改、查找、删除、排序、展示、保存联系人,在我们使用完后联系人还要可以保存在文件中,使用前从文件中将联系人从文件中导入,所以我们有了以下的架构。

 2.头文件&结构体&函数声明

//引头文件
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>


//初始化常量
#define MAX_NAME 20//名字的最大长度
#define MAX_SEX 5//性别的最大长度
#define MAX_TELE 12//电话的最大长度
#define MAX_ADDR 40//地址的最大长度
#define DEFAULT_SZ 3//初始通讯录容量
#define ADD_SZ 2//每次扩容通讯录的量



//创建联系人结构体
struct PeoInfo
{
	char name[MAX_NAME];
	int age;
	char sex[MAX_SEX];
	char tele[MAX_TELE];
	char addr[MAX_ADDR];
};


//创建通讯录结构体
struct Contact
{
	struct PeoInfo* data;//联系人数据
	int capaticy;//当前通讯录容量
	int size;//当前联系人数量
};



//函数声明
void meum();//初始化菜单

void InitContact(struct Contact* ps);//初始化通讯录

void ShowContact(const struct Contact* ps);//展示通讯录

void AddContact(struct Contact* ps);//添加联系人

void DelContact(struct Contact* ps);//删除联系人

void SearchContact(const struct Contact* ps);//查找联系人

void ModifyContact(struct Contact* ps);//修改联系人信息

void SortContact(struct Contact* ps);//排序联系人

void DestoryContact(struct Contact* ps);//销毁通讯录

void SaveContact(struct Contact* ps);//保存通讯录

void LoadContact(struct Contact* ps);//加载通讯录到程序中

void CheckCapacity(struct Contact* ps);//查找通讯录是否需要增容

int FindByNmae(const struct Contact* ps, char* name);//在通讯录中查找联系人

(1)可以看到这里定义了很多宏常量,在后面如果想要修改一些参数,可以直接在#define这里修改,不用在程序里找出来再修改,效率会高很多,也很方便。

(2)这里创造了一个通讯录结构体,又创建了一个通讯录的结构体,成员包括通讯录的容量、联系人数量,还有一个联系人结构体的指针,这是因为如果一开始我们不知道通讯录中会存放多少联系人,就是如果通过创建struct PeoInfo类型的数组,我们不知道这个数组要定义多大,定义太大又不好,太小也不好,所以这里暂时先再通讯录结构体中放一个联系人结构体指针,通过动态内存分配分配通讯录的大小。

3.主函数&通讯录界面

int main()
{
	char input;
	struct Contact Con;//创建通讯录
	InitContact(&Con);//初始化通讯录
	do
	{
		meum();//打印菜单
		setbuf(stdin, NULL);
		input = getch();
		switch(input)
		{
			case '1':
				AddContact(&Con);//添加联系人
				break;
			case '2':
				DelContact(&Con);//删除联系人
				break;
			case '3':
				SearchContact(&Con);//查找联系人
				break;
			case '4':
				ModifyContact(&Con);//修改联系人
				break;
			case '5':
				ShowContact(&Con);//展示通讯录
				break;
			case '6':
				SortContact(&Con);//查找通讯录
				break;
			case '0':
				SaveContact(&Con);//保存通讯录
				DestoryContact(&Con);//退出前free掉通讯录
				break;
			case '7':
				SaveContact(&Con);//保存通讯录
				break;
			default:
				printf("选择错误,请重新选择\n");
				break;
		}
	} while (input!='0');
	return 0;
}
//菜单
void meum()
{
	printf("*********************************************\n");
	printf("*****1.addcontact        2.delcontact   *****\n");
	printf("*****3.searchcontact     4.modifycontact*****\n");
	printf("*****5.showcontact       6.sortcontact  *****\n");
	printf("*****7.save              0.exit         *****\n");
	printf("*********************************************\n");
}

(1)这里用了do—while循环里配合switch能非常好地实现菜单的选择功能,这里的input用%c类型时因为如果input时%d类型的话,在向input中输入的是字符或者字符串时就会陷入死循环,具体的大家可以自行编写尝试。

(2)里边用了一个setbuf函数,功能是清除输入缓冲区,如果一开始input接收的不是%c的内容,

这些内容有一部分就会留在输入缓冲区,导致下一次输入的时候getch直接读取了输入缓冲区的内容,出现错误,所以每次输入前都把输入缓冲区中的内容清空,清除输入缓冲区的方法有很多,这里不细谈,个人感觉setbuf(stdin,NULL)较为方便,stdin是标准输入流,即键盘输入。

4.初始化通讯录&加载已有通讯录

//初始化通讯录
void InitContact(struct Contact* ps)
{
	ps->data = (struct PeoInfo*)malloc(DEFAULT_SZ * sizeof(struct PeoInfo));//用malloc进行动态内存分配,一开始先分配三个联系人的空间。

	if (ps->data == NULL)//若分配失败
	{
		return;
	}

	ps->capaticy = DEFAULT_SZ;//初始化通讯录容量
	ps->size = 0;//初始化联系人数量
	LoadContact(ps);//从文件中加载已有通讯录进程序
}

//加载已有通讯录
void LoadContact(struct Contact* ps)
{
	struct PeoInfo tmp = { 0 };
	FILE* psRead = fopen("Contact.txt", "rb");//以二进制只读的方式打开文件

	if (!psRead)//若打开失败,即psRead为空指针
	{
		printf("LoadContact::%s\n", strerror(errno));
		return;
	}

	while (fread(&tmp, sizeof(struct PeoInfo), 1, psRead))//fread读取成功返回一个非0值,将文件中的数据先读入tmp中
	{
		CheckCapacity(ps);//每次一读取后检查程序中的通讯录是否需要增容
		ps->data[ps->size] = tmp;//通讯录依次接收
		ps->size++;//别忘了通讯录中联系人个数+1
	}

	fclose(psRead);//用完了就关闭文件
	psRead = NULL;//指针置空,避免野指针
}

//检查通讯录是否需要增容
void CheckCapacity(struct Contact* ps)
{
	if (ps->size == ps->capaticy)//如果通讯录的容量刚好等于联系人个数
	{
		struct PeoInfo* ptr = realloc(ps->data, (ps->capaticy + ADD_SZ) * sizeof(struct PeoInfo));//用realloc重新分配通讯录的大小(容纳联系人的个数)

		if (ptr)//如果重新分配成功
		{
			ps->data = ptr;//原来的指针重新指向新分配的大空间
			ps->capaticy += ADD_SZ;
			printf("增容成功!\n");
		}
		else
		{
			printf("增容失败!\n");
		}
	}
}

首先先初始化通讯录的一些数据,再从文件中读取已有的联系人,若一开始通讯录分配的空间不足以容纳,则用relloc再开辟一块大空间存放。

5.通过名字查找联系人

//查找名字
int FindByNmae(const struct Contact* ps,char* name)
{
	int i = 0;
	for (i = 0; i < ps->size; i++)//将用户输入的name在通讯录中依次查找
	{
		if (strcmp(ps->data[i].name, name) == 0)//strcmp找到了相同的name,则返回0
		{
			return i;//找到了是在ps->data中的第几个
		}
	}
	return -1;
}

这个功能在下面会用到很多次,单独包装成一个函数

6.添加联系人

void AddContact(struct Contact* ps)
{
	CheckCapacity(ps);//检查是否需要增容
	printf("请输入名字>");
	scanf("%s", ps->data[ps->size].name);
	printf("请输入年龄>");
	scanf("%d", &(ps->data[ps->size].age));
	printf("请输入性别>");
	scanf("%s", ps->data[ps->size].sex);
	printf("请输入电话>");
	scanf("%s", ps->data[ps->size].tele);
	printf("请输入地址>");
	scanf("%s", ps->data[ps->size].addr);
	ps->size++;
	printf("添加成功\n");

}

这个比较简单

7.删除联系人

//删除联系人
void DelContact(struct Contact* ps)
{
	char name[MAX_NAME]="";
	printf("请输入你要删除的联系人的名字:\n");
	scanf("%s", name);
	int ret = FindByNmae(ps, name);//找到了联系人,返回-1
	if (ret==-1)
	{
		printf("没有找到该联系人\n");
	}
	else
	{
		int j = 0;
		for (j = ret; j < ps->size - 1; j++)//将后面的联系人往前送
		{
			ps->data[j] = ps->data[j + 1];
		}
		ps->size--;
		printf("删除成功\n");
	}
}

就是在列表中找到联系人,再把他删了。

8.查找联系人并将他打印出来

//查找联系人
void SearchContact(const struct Contact* ps)
{
	char name[MAX_NAME];
	printf("请输入要查找的名字");
	scanf("%s", name);
	int ret=FindByNmae(ps, name);
	if (ret == -1)
	{
		printf("没找到");
	}
	else
	{
		printf("找到了!\n");
		printf("%-20s\t%-4s\t%-5s\t%-12s\t%-20s\n", "名字", "年龄", "性别", "电话", "地址");
		printf("%-20s\t%-4d\t%-5s\t%-12s\t%-20s\n",
	    ps->data[ret].name,
		ps->data[ret].age,
		ps->data[ret].sex,
		ps->data[ret].tele,
		ps->data[ret].addr);
	}
}

9.修改联系人信息

//修改联系人信息
void ModifyContact(struct Contact* ps)
{
	char name[MAX_NAME];
	printf("请输入要修改的联系人");
	scanf("%s", name);
	int ret = FindByNmae(ps, name);
	if (ret == -1)
	{
		printf("输入的联系人不存在\n");
	}
	else
	{
		printf("找到了,请重新输入>\n");
		printf("请输入名字>");
		scanf("%s", ps->data[ret].name);
		printf("请输入年龄>");
		scanf("%d", &(ps->data[ret].age));
		printf("请输入性别>");
		scanf("%s", ps->data[ret].sex);
		printf("请输入电话>");
		scanf("%s", ps->data[ret].tele);
		printf("请输入地址>");
		scanf("%s", ps->data[ret].addr);
		printf("修改成功!\n");
	}
}

10.展示通讯录

void ShowContact(const struct Contact* ps)
{
	if (ps->size == 0)
	{
		printf("通讯录为空\n");
	}
	else
	{
		printf("%-20s\t%-4s\t%-5s\t%-12s\t%-20s\n", "名字", "年龄", "性别", "电话", "地址");
		for (int i = 0; i < ps->size; i++)
		{
			printf("%-20s\t%-4d\t%-5s\t%-12s\t%-20s\n",
				ps->data[i].name,
				ps->data[i].age,
				ps->data[i].sex,
				ps->data[i].tele,
				ps->data[i].addr);
		}
	}
}

11.排序通讯录

//排序比较函数
static int Cmp_Contact_Name(const void* e1, const void* e2)
{
	return (((struct PeoInfo*)e2)->age) - (((struct PeoInfo*)e1)->age);
}

//依据年龄排序联系人
void SortContact(struct Contact* ps)
{
	qsort((ps->data), ps->size,sizeof(ps->data[0]), Cmp_Contact_Name);
	printf("排序完成!\n");
}

这里用了qsort快速排序库函数,按照用户的年龄进行排序,也可以按照其它的方式排序,详细的可以看博主的另一篇关于快速排序库函数qsort的介绍的文章。

12.保存通讯录到文件中

//保存通讯录到文件中
void SaveContact(struct Contact* ps)
{
	FILE* pfWrite = fopen("Contact.txt", "wb");//以二进制只写的方式打开文件

	if (!pfWrite)//若打开失败
	{
		printf("SaveContact::%s\n", strerror(errno));//errno可以理解成错误的编码,strerror可以将errno错误编码所对应的错误信息输出出来
		return;
	}
	for (int i = 0; i < ps->size; i++)
	{
		fwrite(&(ps->data[i]), sizeof(struct PeoInfo), 1, pfWrite);//依次将信息写入
	}

	fclose(pfWrite);//写完就关闭文件
	pfWrite = NULL;//置空,避免野指针
	printf("保存成功!\n");
}

13.销毁通讯录(释放内存)

因为这个通讯录在程序中是用动态内存分配出来的,程序结束时要把这块空间free掉,还给系统。

//销毁通讯录-释放堆区内存
void DestoryContact(struct Contact* ps)
{
	free(ps->data);
	ps->data = NULL;
	printf("退出成功!\n");
}

14.!!!!!全部代码!!!!

//引头文件
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>


//初始化常量
#define MAX_NAME 20//名字的最大长度
#define MAX_SEX 5//性别的最大长度
#define MAX_TELE 12//电话的最大长度
#define MAX_ADDR 40//地址的最大长度
#define DEFAULT_SZ 3//初始通讯录容量
#define ADD_SZ 2//每次扩容通讯录的量



//创建联系人结构体
struct PeoInfo
{
	char name[MAX_NAME];
	int age;
	char sex[MAX_SEX];
	char tele[MAX_TELE];
	char addr[MAX_ADDR];
};


//创建通讯录结构体
struct Contact
{
	struct PeoInfo* data;//联系人数据
	int capaticy;//当前通讯录容量
	int size;//当前联系人数量
};



//函数声明
void meum();//初始化菜单

void InitContact(struct Contact* ps);//初始化通讯录

void ShowContact(const struct Contact* ps);//展示通讯录

void AddContact(struct Contact* ps);//添加联系人

void DelContact(struct Contact* ps);//删除联系人

void SearchContact(const struct Contact* ps);//查找联系人

void ModifyContact(struct Contact* ps);//修改联系人信息

void SortContact(struct Contact* ps);//排序联系人

void DestoryContact(struct Contact* ps);//销毁通讯录

void SaveContact(struct Contact* ps);//保存通讯录

void LoadContact(struct Contact* ps);//加载通讯录到程序中

void CheckCapacity(struct Contact* ps);//查找通讯录是否需要增容

int FindByNmae(const struct Contact* ps, char* name);//在通讯录中查找联系人

int main()
{
	char input;
	struct Contact Con;//创建通讯录
	InitContact(&Con);//初始化通讯录
	do
	{
		meum();//打印菜单
		setbuf(stdin, NULL);
		input = getch();
		switch(input)
		{
			case '1':
				AddContact(&Con);//添加联系人
				break;
			case '2':
				DelContact(&Con);//删除联系人
				break;
			case '3':
				SearchContact(&Con);//查找联系人
				break;
			case '4':
				ModifyContact(&Con);//修改联系人
				break;
			case '5':
				ShowContact(&Con);//展示通讯录
				break;
			case '6':
				SortContact(&Con);//查找通讯录
				break;
			case '0':
				SaveContact(&Con);//保存通讯录
				DestoryContact(&Con);//退出前free掉通讯录
				break;
			case '7':
				SaveContact(&Con);//保存通讯录
				break;
			default:
				printf("选择错误,请重新选择\n");
				break;
		}
	} while (input!='0');
	return 0;
}
//菜单
void meum()
{
	printf("*********************************************\n");
	printf("*****1.addcontact        2.delcontact   *****\n");
	printf("*****3.searchcontact     4.modifycontact*****\n");
	printf("*****5.showcontact       6.sortcontact  *****\n");
	printf("*****7.save              0.exit         *****\n");
	printf("*********************************************\n");
}

//初始化通讯录
void InitContact(struct Contact* ps)
{
	ps->data = (struct PeoInfo*)malloc(DEFAULT_SZ * sizeof(struct PeoInfo));//用malloc进行动态内存分配,一开始先分配三个联系人的空间。

	if (ps->data == NULL)//若分配失败
	{
		return;
	}

	ps->capaticy = DEFAULT_SZ;//初始化通讯录容量
	ps->size = 0;//初始化联系人数量
	LoadContact(ps);//从文件中加载已有通讯录进程序
}

//加载已有通讯录
void LoadContact(struct Contact* ps)
{
	struct PeoInfo tmp = { 0 };
	FILE* psRead = fopen("Contact.txt", "rb");//以二进制只读的方式打开文件

	if (!psRead)//若打开失败,即psRead为空指针
	{
		printf("LoadContact::%s\n", strerror(errno));
		return;
	}

	while (fread(&tmp, sizeof(struct PeoInfo), 1, psRead))//fread读取成功返回一个非0值,将文件中的数据先读入tmp中
	{
		CheckCapacity(ps);//每次一读取后检查程序中的通讯录是否需要增容
		ps->data[ps->size] = tmp;//通讯录依次接收
		ps->size++;//别忘了通讯录中联系人个数+1
	}

	fclose(psRead);//用完了就关闭文件
	psRead = NULL;//指针置空,避免野指针
}

//检查通讯录是否需要增容
void CheckCapacity(struct Contact* ps)
{
	if (ps->size == ps->capaticy)//如果通讯录的容量刚好等于联系人个数
	{
		struct PeoInfo* ptr = realloc(ps->data, (ps->capaticy + ADD_SZ) * sizeof(struct PeoInfo));//用realloc重新分配通讯录的大小(容纳联系人的个数)

		if (ptr)//如果重新分配成功
		{
			ps->data = ptr;//原来的指针重新指向新分配的大空间
			ps->capaticy += ADD_SZ;
			printf("增容成功!\n");
		}
		else
		{
			printf("增容失败!\n");
		}
	}
}

//查找名字
int FindByNmae(const struct Contact* ps,char* name)
{
	int i = 0;
	for (i = 0; i < ps->size; i++)//将用户输入的name在通讯录中依次查找
	{
		if (strcmp(ps->data[i].name, name) == 0)//strcmp找到了相同的name,则返回0
		{
			return i;//找到了是在ps->data中的第几个
		}
	}
	return -1;
}
//添加联系人
void AddContact(struct Contact* ps)
{
	CheckCapacity(ps);//检查是否需要增容
	printf("请输入名字>");
	scanf("%s", ps->data[ps->size].name);
	printf("请输入年龄>");
	scanf("%d", &(ps->data[ps->size].age));
	printf("请输入性别>");
	scanf("%s", ps->data[ps->size].sex);
	printf("请输入电话>");
	scanf("%s", ps->data[ps->size].tele);
	printf("请输入地址>");
	scanf("%s", ps->data[ps->size].addr);
	ps->size++;
	printf("添加成功\n");

}

//删除联系人
void DelContact(struct Contact* ps)
{
	char name[MAX_NAME]="";
	printf("请输入你要删除的联系人的名字:\n");
	scanf("%s", name);
	int ret = FindByNmae(ps, name);//找到了联系人,返回-1
	if (ret==-1)
	{
		printf("没有找到该联系人\n");
	}
	else
	{
		int j = 0;
		for (j = ret; j < ps->size - 1; j++)//将后面的联系人往前送
		{
			ps->data[j] = ps->data[j + 1];
		}
		ps->size--;
		printf("删除成功\n");
	}
}

//查找联系人
void SearchContact(const struct Contact* ps)
{
	char name[MAX_NAME];
	printf("请输入要查找的名字");
	scanf("%s", name);
	int ret=FindByNmae(ps, name);
	if (ret == -1)
	{
		printf("没找到");
	}
	else
	{
		printf("找到了!\n");
		printf("%-20s\t%-4s\t%-5s\t%-12s\t%-20s\n", "名字", "年龄", "性别", "电话", "地址");
		printf("%-20s\t%-4d\t%-5s\t%-12s\t%-20s\n",
	    ps->data[ret].name,
		ps->data[ret].age,
		ps->data[ret].sex,
		ps->data[ret].tele,
		ps->data[ret].addr);
	}
}

//展示通讯录
void ShowContact(const struct Contact* ps)
{
	if (ps->size == 0)
	{
		printf("通讯录为空\n");
	}
	else
	{
		printf("%-20s\t%-4s\t%-5s\t%-12s\t%-20s\n", "名字", "年龄", "性别", "电话", "地址");
		for (int i = 0; i < ps->size; i++)
		{
			printf("%-20s\t%-4d\t%-5s\t%-12s\t%-20s\n",
				ps->data[i].name,
				ps->data[i].age,
				ps->data[i].sex,
				ps->data[i].tele,
				ps->data[i].addr);
		}
	}
}

//排序比较函数
static int Cmp_Contact_Name(const void* e1, const void* e2)
{
	return (((struct PeoInfo*)e2)->age) - (((struct PeoInfo*)e1)->age);
}

//依据年龄排序联系人
void SortContact(struct Contact* ps)
{
	qsort((ps->data), ps->size,sizeof(ps->data[0]), Cmp_Contact_Name);
	printf("排序完成!\n");
}

//保存通讯录到文件中
void SaveContact(struct Contact* ps)
{
	FILE* pfWrite = fopen("Contact.txt", "wb");//以二进制只写的方式打开文件

	if (!pfWrite)//若打开失败
	{
		printf("SaveContact::%s\n", strerror(errno));//errno可以理解成错误的编码,strerror可以将errno错误编码所对应的错误信息输出出来
		return;
	}
	for (int i = 0; i < ps->size; i++)
	{
		fwrite(&(ps->data[i]), sizeof(struct PeoInfo), 1, pfWrite);//依次将信息写入
	}

	fclose(pfWrite);//写完就关闭文件
	pfWrite = NULL;//置空,避免野指针
	printf("保存成功!\n");
}

//销毁通讯录-释放堆区内存
void DestoryContact(struct Contact* ps)
{
	free(ps->data);
	ps->data = NULL;
	printf("退出成功!\n");
}

看完了不妨点点赞吧~

猜你喜欢

转载自blog.csdn.net/weixin_62244902/article/details/122200659