[C]实现能在本地存储的简易通讯录

作者 华丞臧.
专栏【C语言】
各位读者老爷如果觉得博主写的不错,请诸位多多支持(点赞+收藏+关注)。如果有错误的地方,欢迎在评论区指出。



一、文件动态通讯录

前面写过一个静态通讯录,静态通讯录的容量是固定的,存放联系人达到最大容量就不能再存放了,并且在程序运行结束通讯录就销毁了;静态通讯录缺点很多,既不能根据用户需求扩容又不能在本地保存。

这次在学习相关文件操作后,我们来尝试写一个可以再本地文件保存数据的可动态增长的通讯录。

二、相关代码解析

2.1 初始化

初始化函数不仅要初始化data数组,还需要把本地保存的联系人数据加载到通讯录当中;而加载数据到data数组中去需要我们在堆上开辟空间,并且我们并不知道本地存储了多少字节的数据,所以写一个检查扩容的函数,当data数组没有开辟内存或者存满了就扩容,一次扩容两倍。

使用的是fread库函数,其函数原型如下:
在这里插入图片描述

//检查扩容
void CheckContactCapacity(CT* con)
{
	assert(con);

	//判断是否需要扩容
	if (con->size == con->capacity)
	{
		int newCapacity = con->capacity * 2;
		PeoInfo* tmp = (PeoInfo*)realloc(con->data, newCapacity * sizeof(PeoInfo));
		if (tmp == NULL)
		{
			perror("realloc fail");
			exit(-1);
		}
		con->data = tmp;
		con->capacity = newCapacity;
		printf("扩容成功\n");
	}	
}


//初始化
void ContactInit(CT* con)
{
	assert(con);
	
	con->data = (PeoInfo*)malloc(4 * sizeof(PeoInfo));
	con->size =  0;
	con->capacity = 4;
	//memset(con->data, 0, con->capacity * sizeof(PeoInfo));

	FILE* pf = fopen("contact.txt", "rb");
	PeoInfo s = { 0 };
	while (fread(&s, sizeof(PeoInfo), 1, pf))
	{
		CheckContactCapacity(con);
		con->data[con->size] = s;
		con->size++;
	}
	fclose(pf);
}

2.2 销毁通讯录

销毁通讯录,把通讯录当中的数据写入到本地文件当中并且释放动态内存开辟的空间。
在这里我使用的是fwrite库函数,其函数原型如下:
在这里插入图片描述

//通讯录销毁
void ContactDestroy(CT* con)
{
	assert(con);
	if (con->size != 0)
	{
		FILE* pf = fopen("contact.txt", "wb");

		int i = 0;
		PeoInfo s = { 0 };
		while (i < con->size)
		{
			s = con->data[i];
			fwrite(&s, sizeof(PeoInfo), 1, pf);
			i++;
		}

		fclose(pf);
	}

	free(con->data);
	con->data = NULL;
	con->size = con->capacity = 0;
}

2.3 增加联系人

通讯录是用来存储联系人信息的,那么就必然需要等一系列接口,通过这些接口来对通讯录进行操作。那么在通讯录中增加一个新的联系人,首先需要判断是否需要扩容;在初始化函数当中已经写了CheckContactCapacity()的函数直接调用即可,然后输入要增加的人的信息依次输入,用scanf函数存入对应的data数组当中。

//增加
void ContactAdd(CT* con)
{
	assert(con);
	
	//判断是否需要扩容
	CheckContactCapacity(con);
	
	//输入联系人信息
	printf("请输入姓名->");
	scanf("%s",con->data[con->size].name);

	printf("请输入性别->");
	scanf("%s", con->data[con->size].sex);

	printf("请输入年龄->");
	scanf("%d", &(con->data[con->size].age));

	printf("请输入电话->");
	scanf("%s", con->data[con->size].tele);

	printf("请输入地址->");
	scanf("%s", con->data[con->size].addr);
	
	con->size++;
}

2.4 查找指定联系人

查找指定联系人,需要知道该联系人至少一个信息。既然是按照姓名查找,就写一个FindByName()函数用来查找联系人,找到了返回下标找不到返回-1;找到了,就根据返回的下标把其对应的信息打印出来。


//按照名字查找,找到返回下标,找不到返回-1
int FindByName(CT* con,char* name)
{
	assert(con);
	
	PeoInfo* cur = con->data;

	//遍历查找
	int i = 0;
	while (i < con->size)
	{
		if (strcmp(name, (cur + i)->name) == 0)
		{
			return i;  //
		}
		i++;
	}
	return -1;
}

//查找
void ContactFind(CT* con)
{
	assert(con);

	char* name[NAME_MAX] = { 0 };

	printf("请输入要查找人的姓名->");
	scanf("%s",name);

	int n = FindByName(con,name);
	if (-1 == n)
	{
		printf("查找的人不存在\n");
		return;
	}

	printf("%-20s\t %-5s\t %-3s\t %-12s\t %-30s\t\n", 
		"姓名", 
		"性别", 
		"年龄", 
		"电话", 
		"地址");

	printf("%-20s\t %-5s\t %-3d\t %-12s\t %-30s\t\n"
		, con->data[n].name
		, con->data[n].sex
		, con->data[n].age
		, con->data[n].tele
		, con->data[n].addr);
}

2.5 删除指定联系人

通过姓名删除指定联系人,调用FindByName()函数找出要删除的人的下标,再挪动数据data数组处于下标位置的数据就可以达到删除的效果。

//删除
void ContactDel(CT* con)
{
	assert(con);
	assert(con->size);

	char name[NAME_MAX] = { 0 };

	printf("请输入要删除人的姓名->");
	scanf("%s",name);

	//查找
	int  n = FindByName(con, name);
	if (-1 == n)
	{
		printf("删除的人不存在\n");
	}
	else
	{
		//挪动数据
		while (n < con->size - 1)
		{
			con->data[n] = con->data[n + 1];
			n++;
		}

		con->size--;
	}
	printf("删除成功\n");
}

2.6 修改指定联系人

这里也是通过姓名来查找并且修改联系人的信息,同样调用FindByName()函数找出要修改的练习人的下标,通过返回的下标对其进行修改操作。

//修改
void ContactModify(CT* con)
{
	assert(con);

	char* name[NAME_MAX] = { 0 };

	printf("请输入要修改人的姓名->");
	scanf("%s",name);

	int n = FindByName(con, name);
	if (-1 == n)
	{
		printf("要修改的人不存在\n");
		return;
	}

	//输入联系人信息
	printf("请输入姓名->");
	scanf("%s", con->data[n].name);

	printf("请输入性别->");
	scanf("%s", con->data[n].sex);

	printf("请输入年龄->");
	scanf("%d", &(con->data[n].age));

	printf("请输入电话->");
	scanf("%s", con->data[n].tele);

	printf("请输入地址->");
	scanf("%s", con->data[n].addr);

	printf("修改成功\n");
}

2.7 通讯录清空

通讯录清空只需要把con->size置为0即可,con->size表示data数组中有效数据的个数。

//清空
void ContactClear(CT* con)
{
	assert(con);

	con->size = 0;
	
	printf("清空成功\n");
}

2.8 按照姓名排序

利用qsort函数,按照姓名对data中存放的数据进行排序;这里需要我们写一个比较字符串大小的函数,而strcmp()是用来比较字符串大小的库函数,且其返回值与qsort的要求一致。
qsort的说明和使用可见👉:qsort的使用和实现

int cmp_by_name(char* str1, char* str2)
{
	return strcmp(str1, str2);
}

//按照姓名排序
void ContactSortByName(CT* con)
{
	assert(con);

	qsort(con->data, con->size, sizeof(PeoInfo), cmp_by_name);

	ContactShow(con);
}

三、代码

3.1 main.c

#include "Contact.h"

void menu()
{
	printf("***** ***** ***** ***** *****\n");
	printf("***** 0.退出     1.添加 *****\n");
	printf("***** 2.删除     3.查找 *****\n");
	printf("***** 4.修改     5.展示 *****\n");
	printf("***** 6.排序     7.清空 *****\n");
	printf("***** ***** ***** ***** *****\n");
}

int main()
{
	
	CT con;
	ContactInit(&con);

	int input = 0;
	do
	{
		menu();
		printf("请选择->");
		scanf("%d",&input);
		switch (input)
		{
		case EXIT:
			ContactDestroy(&con);
			break;
		case ADD:
			ContactAdd(&con);
			break;
		case DEL:
			ContactDel(&con);
			break;
		case SEARCH:
			ContactSearch(&con);
			break;
		case MODIFY:
			ContactModify(&con);
			break;
		case SHOW:
			ContactShow(&con);
			break;
		case SORT:
			ContactSortByName(&con);
			break;
		case CLEAR:
			ContactClear(&con);
			break;
		default:
			break;
		}
	} while (input);
	return 0;
}

3.2 Contact.h

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>


#define NAME_MAX 20
#define SEX_MAX 5
#define TELE_MAX 12
#define ADDR_MAX 30


typedef struct PeopleInfo
{
	char name[NAME_MAX];
	char sex[SEX_MAX];
	int age;
	char* tele[TELE_MAX];
	char* addr[ADDR_MAX];
}PeoInfo;

typedef struct Contact
{
	PeoInfo* data;
	int size;     //联系人个数
	int capacity; //容量
}CT;

enum Operations
{
	EXIT,
	ADD,
	DEL,
	SEARCH,
	MODIFY,
	SHOW,
	SORT,
	CLEAR
};

//初始化
void ContactInit(CT* con);

//打印通讯录信息
void ContactShow(CT* con);

//增加
void ContactAdd(CT* con);

//删除
void ContactDel(CT* con);

//查找
void ContactSearch(CT* con);

//修改
void ContactModify(CT* con);

//清空
void ContactClear(CT* con);

//按照姓名排序
void ContactSortByName(CT* con);

//通讯录销毁
void ContactDestroy(CT* con);

3.3 Contact.c

#include "Contact.h"

//检查扩容
void CheckContactCapacity(CT* con)
{
	assert(con);

	//判断是否需要扩容
	if (con->size == con->capacity)
	{
		int newCapacity = con->capacity * 2;
		PeoInfo* tmp = (PeoInfo*)realloc(con->data, newCapacity * sizeof(PeoInfo));
		if (tmp == NULL)
		{
			perror("realloc fail");
			exit(-1);
		}
		con->data = tmp;
		con->capacity = newCapacity;
		//memset(tmp+con->size, 0, (con->capacity-con->size) * sizeof(PeoInfo));
		printf("扩容成功\n");
	}	
}


//初始化
void ContactInit(CT* con)
{
	assert(con);
	
	con->data = (PeoInfo*)malloc(4 * sizeof(PeoInfo));
	con->size =  0;
	con->capacity = 4;
	//memset(con->data, 0, con->capacity * sizeof(PeoInfo));

	FILE* pf = fopen("contact.txt", "rb");
	PeoInfo s = { 0 };
	while (fread(&s, sizeof(PeoInfo), 1, pf))
	{
		CheckContactCapacity(con);
		con->data[con->size] = s;
		con->size++;
	}

	fclose(pf);

}
//打印通讯录信息
void ContactShow(CT* con)
{
	assert(con);

	PeoInfo* cur = con->data;

	//遍历打印
	printf("%-20s\t %-5s\t %-3s\t %-12s\t %-30s\t\n", "姓名", "性别", "年龄", "电话", "地址");
	int i = 0;
	while (i < con->size)
	{
		printf("%-20s\t %-5s\t %-3d\t %-12s\t %-30s\t\n", con->data[i].name
		, con->data[i].sex
		, con->data[i].age
		, con->data[i].tele
		, con->data[i].addr);
		i++;
	}
}

//增加
void ContactAdd(CT* con)
{
	assert(con);
	
	//判断是否需要扩容
	CheckContactCapacity(con);
	
	
	//输入联系人信息
	printf("请输入姓名->");
	scanf("%s",con->data[con->size].name);

	printf("请输入性别->");
	scanf("%s", con->data[con->size].sex);

	printf("请输入年龄->");
	scanf("%d", &(con->data[con->size].age));

	printf("请输入电话->");
	scanf("%s", con->data[con->size].tele);

	printf("请输入地址->");
	scanf("%s", con->data[con->size].addr);
	
	con->size++;
}

//按照名字查找,找到返回下标,找不到返回-1
int FindByName(CT* con,char* name)
{
	assert(con);
	
	PeoInfo* cur = con->data;

	//遍历查找
	int i = 0;
	while (i < con->size)
	{
		if (strcmp(name, cur->name) == 0)
		{
			return i;  //
		}
		i++;
	}
	return -1;
}

//删除
void ContactDel(CT* con)
{
	assert(con);
	assert(con->size);

	char name[NAME_MAX] = { 0 };

	printf("请输入要删除人的姓名->");
	scanf("%s",name);

	//查找
	int  n = FindByName(con, name);
	if (-1 == n)
	{
		printf("删除的人不存在\n");
	}
	else
	{
		//挪动数据
		while (n < con->size - 1)
		{
			con->data[n] = con->data[n + 1];
			n++;
		}

		con->size--;
	}
	printf("删除成功\n");
}


//查找
void ContactSearch(CT* con)
{
	assert(con);

	char* name[NAME_MAX] = { 0 };

	printf("请输入要查找人的姓名->");
	scanf("%s",name);

	int n = FindByName(con,name);
	if (-1 == n)
	{
		printf("查找的人不存在\n");
		return;
	}

	printf("%-20s\t %-5s\t %-3s\t %-12s\t %-30s\t\n", 
		"姓名", 
		"性别", 
		"年龄", 
		"电话", 
		"地址");

	printf("%-20s\t %-5s\t %-3d\t %-12s\t %-30s\t\n"
		, con->data[n].name
		, con->data[n].sex
		, con->data[n].age
		, con->data[n].tele
		, con->data[n].addr);
}

//修改
void ContactModify(CT* con)
{
	assert(con);

	char* name[NAME_MAX] = { 0 };

	printf("请输入要修改人的姓名->");
	scanf("%s",name);

	int n = FindByName(con, name);
	if (-1 == n)
	{
		printf("要修改的人不存在\n");
		return;
	}
	
		//输入联系人信息
		printf("请输入姓名->");
		scanf("%s", con->data[n].name);

		printf("请输入性别->");
		scanf("%s", con->data[n].sex);

		printf("请输入年龄->");
		scanf("%d", &(con->data[n].age));

		printf("请输入电话->");
		scanf("%s", con->data[n].tele);

		printf("请输入地址->");
		scanf("%s", con->data[n].addr);
		printf("修改成功\n");
}


//清空
void ContactClear(CT* con)
{
	assert(con);

	con->size = 0;

	printf("清空成功\n");
}

int cmp_by_name(char* str1, char* str2)
{
	return strcmp(str1, str2);
}

//按照姓名排序
void ContactSortByName(CT* con)
{
	assert(con);

	qsort(con->data, con->size, sizeof(PeoInfo), cmp_by_name);

	ContactShow(con);
}


//通讯录销毁
void ContactDestroy(CT* con)
{
	assert(con);
	if (con->size != 0)
	{
		FILE* pf = fopen("contact.txt", "wb");

		int i = 0;
		PeoInfo s = { 0 };
		while (i < con->size)
		{
			s = con->data[i];
			fwrite(&s, sizeof(PeoInfo), 1, pf);
			i++;
		}

		fclose(pf);
	}

	free(con->data);
	con->data = NULL;
	con->size = con->capacity = 0;
}
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

华丞臧.

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

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

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

打赏作者

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

抵扣说明:

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

余额充值