简易通讯录的实现

涉及知识:文件操作,动态内存管理,结构体,枚举

设计思路:

1.创建两个结构体,一个存放联系人,一个是通讯录实体,包括有效人数,存放联系人结构体的指针,容量(方便与有效人数对比来开辟空间)

2.初始化结构体,为了实现数据的保留用了文件操作,在每次进入通讯录前(也就是初始化阶段)读取文件内容到指定的变量中,然后给变量通讯录赋值,  初始化函数 init_contact

3.增删查改的实现ADD_contact,Dele_contact,Revise_contact,Search_contact

中途涉及里查找名字的Find_by_name,以及展示通讯的Show_contact

4.退出通讯录的时候记得保存数据,也就是把数据写到文件中 Destroy_contact

整体源文件分为三份,contact.h用来存放所有头文件,函数的声明,宏定义,定义的结构体;

contact.c用来存放函数的实现

test.c是main函数位置,是测试源文件

程序涉及重点:

1.每个传递指针的函数都用到了assert(指针),assert的头文件是<assert.h>,作用是断言空指针,如果传递的是空指针,程序会报错并调到该地方,因为函数涉及的修改不能是空指针,否则程序会崩溃。

2.程序部分地方用了strerror(errno)这种形式,errno是一个错误码,在<errno.h>内,是一个全局变量,作用是程序发生错误时,把错误码记录下来,也就是赋值给errno这个全局,然后我们用printf("相关函数的相关操作::%s\n",strerrno(errno));来把错误原因和错误的地方打印出来,方便查找。

3.再让用户输入值的时候,用了一个变量input,然后我们对input 使用switch来判断该进行什么操作,就会出现case 1进入增加函数,case2进入删除函数这些,代码可读性差,因此用到了枚举

把ADD,Dele这些操作变成常数,然后 case ADD,case Dele就简单明了多了,毕竟文件分开两部分写,有时查bug忘记了1的作用是什么很麻烦,也方便后续维护

4.为了不占用太多的内存,采用了动态开辟内存的方式,开多少用contack.h里面宏定义的DEFAULT_SIZE来控制,然后增加联系人的时候与最大容量比较,如果发现不够用了内存就用realloc函数重新开一次,开大点,不过在删除的时候未实现删除的时候改小内存空间

5.初始化阶段 打开文件把数据读到我们程序中的时候,肯定是要把整个文件读完,那怎么表示文件读取结束

可以采用fread函数,二进制的读,

 fread(读到哪里,一次读多大,读多少个,流(就是哪个文件))ps:文件流是流的一种,我们的键盘是标准输入流,显示器是标准输出流

fread有一个好处就是他的返回值是他读取的个数,如果没读取到就是返回0,这是就是文件读完了

所以我们可以用           就是每次读取都放进buffer内部,然后检测一下通讯录够不够大,最后把内容放进通讯录的指针指向的空间内(我们malloc开辟的)

while (fread(&buffer, sizeof(PeoInfo), 1, pr))//fread读取成功返回读取的数量,读取失败返回0
	{
		check_capacity(con);
		con->date[con->sz] = buffer;
		con->sz++;
	}

以下是头文件-》定义了枚举变量,提高代码可读性,#define宏定义了一些大小,如联系人名字的char name数组开多大这些,方便后续修改,以及两个结构体的声明

//函数声明,头文件,各种define定义
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<errno.h>
#include<assert.h>
enum
{
	Exit,
	ADD,
	Delete,
	Search,
	Revise,
	Show,
	Sort,
	All_Delete,
};
#define NAME_MAX 15
#define SEX_MAX   5
#define TELE_MAX  12
#define ADDRESS_MAX 25
#define DEFAULT_SIZE 3

typedef struct
{
	char name[NAME_MAX];
	int age;
	char sex[SEX_MAX];
	char tele[TELE_MAX];
	char address[ADDRESS_MAX];
}PeoInfo;//存储存放人的信息——姓名,年龄,性别,电话,住址

typedef struct
{
	PeoInfo* date;//指向一个动态内存开辟出来的空间,存放通讯录实体
	int sz;//存放人的个数
	int capacity;//开辟的空间最多可以放几个人
}Contact;//通讯录结构体

//初始化的时候从文件中读取数据,根据内容初始化
//通讯录的起始数据(可以保存数据)
void init_contact(Contact* con);
void load_conotact(Contact* con);
void check_capacity(Contact* con);

//增删查改
void ADD_contact(Contact* con);
void Dele_contact(Contact* con);
int Find_by_name(Contact* con, char arr[]);
void Search_contact(Contact* con);
void Revise_contact(Contact* con);
//展示
void Show_contact(Contact* con);
//销毁
void destroy_contact(Contact* con);

//全部删除
void add_delete(Contact* con);

//根据姓名排序
void sort_by_name(Contact* con);

以下是test.c源文件

#include"contact.h"
void menu()
{
	printf("****************************\n");
	printf("******1.ADD      2.Delete***\n");
	printf("******3.Search   4.Revise***\n");
	printf("******5.Show     6.Sort*****\n");
	printf("******7.All_delete 0.Exit*****\n");
	printf("****************************\n");
}

void test()
{
	Contact con = { 0 };
	init_contact(&con);
	int input;
	do 
	{
		menu();
		printf("请选择\n");
		scanf("%d", &input);
		switch (input)
		{
		case ADD:
			ADD_contact(&con);
			break;
		case Delete:
			Dele_contact(&con);
			break;
		case Search:
			Search_contact(&con);
			break;
		case Revise:
			Revise_contact(&con);
			break;
		case Show:
			Show_contact(&con);
			break;
		case Sort:
			sort_by_name(&con);
			break;
		case All_Delete:
			all_delete(&con);
			break;
		case Exit:
			printf("退出通讯录\n");
			destroy_contact(&con);
			break;
		default:
			printf("输入错误,请重新输入\n");
			break;
		}
	} while (input);
}
int main()
{
	test();
	return 0;
}

以下是contact.c

#include"contact.h"

void check_capacity(Contact* con)
{
	assert(con);//防止指针为空
	if (con->capacity == con->sz)
	{
		PeoInfo* tmp = (PeoInfo*)realloc(con->date, sizeof(PeoInfo) * (con->capacity + 2));
		if (tmp != NULL)
		{
			printf("增容成功\n");
			con->date = tmp;
			con->capacity += 2;
		}
		else
		{
			printf("check_capacity->增容:%s\n", strerror(errno));
		}
	}
}

void Load_contact(Contact* con)
{
	assert(con);
	FILE* pr = fopen("contact.txt", "rb");
	if (pr == NULL)
	{
		printf("Load_contact open::%s\n", strerror(errno));
		return;
	}
	PeoInfo buffer = { 0 };
	while (fread(&buffer, sizeof(PeoInfo), 1, pr))//fread读取成功返回读取的数量,读取失败返回0
	{
		check_capacity(con);
		con->date[con->sz] = buffer;
		con->sz++;
	}
	fclose(pr);
	pr = NULL;
}
void init_contact(Contact* con)//初始化通讯录,是有效人数sz=0,
//pf指向一个动态开辟的空间,以及开辟的空间容量
{
	assert(con);
	con->sz = 0;
	PeoInfo* tmp = (PeoInfo*)malloc(sizeof(PeoInfo) * DEFAULT_SIZE);
	if (tmp == NULL)
	{
		printf("init_contact::%s\n", strerror(errno));
		return;
	}
	else
	{
		con->date = tmp;
	}
	con->capacity = DEFAULT_SIZE;
	Load_contact(con);
}

void ADD_contact(Contact* con)
{
	assert(con);
	check_capacity(con);

	printf("请输入姓名\n");
	scanf("%s", con->date[con->sz].name);
	printf("请输入年龄\n");
	scanf("%d", &con->date[con->sz].age);
	printf("请输入性别\n");
	scanf("%s", con->date[con->sz].sex);
	printf("请输入电话号码\n");
	scanf("%s", con->date[con->sz].tele);
	printf("请输入地址\n");
	scanf("%s", con->date[con->sz].address);
	con->sz++;
	printf("增加联系人成功\n");
}

int Find_by_name(Contact* con, char name[])
{
	assert(con);
	for (int i = 0; i < con->sz; i++)
	{
		if (strcmp(con->date[i].name, name) == 0)
			return i;
	}
	return -1;
}

void Dele_contact(Contact* con)
{
	assert(con);
	if (con->sz == 0)
	{
		printf("通讯录为空,不能删除\n");
		return;
	}
	char name[NAME_MAX] = { 0 };
	printf("请输入删除者的姓名\n");
	scanf("%s", name);
	int ret = Find_by_name(con, name);
	if (ret + 1)//失败返回-1,+1就变成0,其他为真都会打印,打印ret也就是找到的人的坐标
	{
		memmove(&con->date[ret], &con->date[ret + 1], sizeof(PeoInfo) * (con->sz - ret));
		con->sz--;
		printf("删除联系人成功\n");
	}
	else
	{
		printf("联系人不存在\n");
	}
}

void Search_contact(Contact* con)
{
	assert(con);
	char name[NAME_MAX] = { 0 };
	printf("请输入查找人的姓名\n");
	scanf("%s", name);
	int ret = Find_by_name(con, name);
	if (ret + 1)
	{
		printf("%-10s %-5d %-5s %-15s %-3s\n",
			con->date[ret].name,
			con->date[ret].age,
			con->date[ret].sex,
			con->date[ret].tele,
			con->date[ret].address);
	}
	else
	{
		printf("不存在该联系人\n");
	}

}

void Revise_contact(Contact* con)
{
	assert(con);
	char name[NAME_MAX] = { 0 };
	if (con->sz == 0)
	{
		printf("通讯录为空,无法修改\n");
		return;
	}
	printf("请输入要修改的人的姓名\n");
	scanf("%s", name);
	int ret = Find_by_name(con, name);
	if (ret + 1)
	{
		memset(&con->date[ret], 0, sizeof(PeoInfo));//重置空间为0
		printf("请重新输入姓名\n");
		scanf("%s", con->date[ret].name);
		printf("请重新输入年龄\n");
		scanf("%d", &con->date[ret].age);
		printf("请重新输入性别\n");
		scanf("%s", con->date[ret].sex);
		printf("请重新输入电话号码\n");
		scanf("%s", con->date[ret].tele);
		printf("请重新输入地址\n");
		scanf("%s", con->date[ret].address);
		printf("修改成功\n");
	}
	else
	{
		printf("该联系人不存在\n");
	}
}
void Show_contact(Contact* con)
{
	assert(con);
	for (int i = 0; i < con->sz; i++)
	{
		printf("%-10s %-5d %-5s %-15s %-3s\n",
			con->date[i].name,
			con->date[i].age,
			con->date[i].sex,
			con->date[i].tele,
			con->date[i].address);
	}
}

void destroy_contact(Contact* con)
{
	FILE* tmp = fopen("contact.txt", "wb");
	if (tmp == NULL)
	{
		printf("destroy_contact 文件保存::%s\n", strerror(errno));
		return;
	}
	for (int i = 0; i < con->sz; i++)
	{
		fwrite(&con->date[i], sizeof(PeoInfo), 1, tmp);
	}
	fclose(tmp);
	tmp = NULL;
}

void all_delete(Contact* con)
{
	assert(con);
	con->capacity = 0;
	con->sz = 0;
	free(con->date);
	printf("删除成功\n");
}


//写个qsort的比较函数的指针
void compare_by_name(const char* p1, const char* p2)
{
	assert(p1 && p2);
	while (*p1 == *p2 && *p1)
	{
		p1++;
		p2++;
	}
	return (unsigned int)(*p1) - (unsigned int)(*p2);
}
//直接用库函数qsort
void sort_by_name(Contact* con)
{
	assert(con);
	qsort(con->date, con->sz, sizeof(PeoInfo), compare_by_name);
	printf("排序成功\n");
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值