【C语言】第四十二弹---一万六千字教你从0到1实现通讯录

 ✨个人主页: 熬夜学编程的小林

💗系列专栏: 【C语言详解】 【数据结构详解】

目录

1、通讯录分析和设计

1.1、通讯录的功能说明

1.2、程序的分析和设计

1.2.1、数据结构的分析

1.2.2、文件结构设计

2、通讯录的结构分析

2.1、创建通讯录结构

2.1.1、静态版本

2.1.2、动态版本

2.2、用户选择

2.3、初始化通讯录

2.3.1、静态通讯录

2.3.2、动态通讯录

2.3.3、动态文件通讯录

2.4、添加联系人信息

2.5、打印联系人信息

2.6、删除联系人

2.7、 查找联系人

2.8、修改联系人

2.9、排序联系人信息

2.10、存储联系人信息到文件

2.11、销毁通讯录

3、完整代码

3.1、contact.h

3.2、contact.c 

3.3、test.c

总结


前面接近四十弹内容基本都在学习C语言的基本语法,单纯的学习语法会让我们的学习比较枯燥,因此该节内容通过C语言学习的语法知识实现一个我们手机都有的通讯录功能。

1、通讯录分析和设计


1.1、通讯录的功能说明

1、通讯录中包含个人的姓名,性别,年龄,电话与地址。

2、用户可以自由进出程序。

3、用户可以自由增删查改以及排序通讯录中的数据。

程序的界面:

1.2、程序的分析和设计


1.2.1、数据结构的分析

通讯录中存储的数据有姓名,性别,年龄,电话与地址,此处会用到字符串(字符串通过字符数组进行实现),整型的结合,因此我们可以封装成一个结构体进行统一存储。根据通讯录功能的说明,我们需要对通讯录进行增删改查操作,如何才能确定需要操作的数据是哪一组呢?如何确定最终存储了多少组数据呢?

解决办法如下:

1、再创建一个结构体,该结构体的内容包括存储数据的结构体和该结构体的最大存储个数(静态版本,创建静态数组)。

2、再创建一个结构体,该结构体的内容包括存储数据的结构体、存储数据的结构体的实际存储个数以及存储数据的结构体的(容量)最大存储个数(动态版本,动态开辟数组)。

知道数据的存储之后就是创建我们常用的菜单,即前面扫雷,猜数字游戏用到的菜单,通讯录也是同样的原理,用do while循环实现此菜单,根据上面程序的界面可以知道,我们需要实现接口函数较多,因此推荐使用switch选择语句,但是switch语句的类型需要整型,单纯我们整数去实现该函数不够通俗易懂,此处的解决办法有1、通过#define定义常量 2、使用枚举类型

如果使用#define定义常量,需要7处,代码也比较繁琐,但是如果此处使用枚举类型,可以定义一次即可(枚举类型不初始化第一个变量自动初始化为0,后面依次加+),非常满足此处的需求

后面就是基本接口函数的实现了,在后面一一详细讲解。


1.2.2、文件结构设计


之前学习了多文件的形式对函数的声明和定义,这里我们实践⼀下,我们设计三个⽂件:

test.c : 文件中写程序的测试逻辑

Contact.c : 文件中写程序中函数的实现等

Contact.h : 件中写程序需要的数据类型和函数声明等

建议:写一些代码就测试一些代码。

2、通讯录的结构分析

2.1、创建通讯录结构

2.1.1、静态版本

静态通讯录就是在开辟空间时,给一个固定的大小缺陷在于空间小了,不能扩容,空间大了,会造成浪费。

//常量使用#define宏定义,便于以后修改

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

#define MAX 100  //通讯录大小,存放多少人的信息
//一个人的信息
typedef struct PeoInfo
{
	char name[NAME_MAX];
	int age;
	char sex[SEX_MAX];
	char tele[TELE_MAX];
	char addr[ADDR_MAX];
}PeoInfo;

//静态版本,通讯录的信息
typedef struct Contact
{
	PeoInfo data[MAX];
	int sz;
}Contact;

2.1.2、动态版本

动态通讯录就是对静态通讯录的缺陷进行改进通过动态内存管理来开辟空间,减少空间浪费的问题。

//常量使用#define宏定义,便于以后修改

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

//一个人的信息
typedef struct PeoInfo
{
	char name[NAME_MAX];
	int age;
	char sex[SEX_MAX];
	char tele[TELE_MAX];
	char addr[ADDR_MAX];
}PeoInfo;

typedef struct Contact
{
	PeoInfo* data;
	int sz;
	int capacity;
}Contact;

2.2、用户选择

首先用户选择需要的功能,输入1则添加信息,输入2则删除信息,输入3则查询信息,输入4则修改信息,输入5则展示信息,输入6则排序信息,输入0则退出游戏输入其他值则重新输入。

从这可以知道此处为一个循环,而且一定会进入一次,符号do while的特性,因此使用do while循环,但是此处需要打印一个选项的界面,因此可以使用创建一个菜单。

为了更好的表示switch中每个分支的意思,此处使用枚举变量来表示!!! 

主体函数如下:

该结构中是包含文件版本的,在退出程序之前会保存联系人信息。

void menu()
{
	printf("********************************************\n");
	printf("**********   1.add      2.del     **********\n");
	printf("**********   3.search   4.modify  **********\n");
	printf("**********   5.show     6.sort    **********\n");
	printf("**********   0.exit               **********\n");
	printf("********************************************\n");
}

//增强代码可读性,使用枚举变量
enum Option
{
	EXIT,//退出游戏,默认值为0
	ADD,//添加联系人信息
	DEL,//删除联系人信息
	SEARCH,//查找联系人信息
	MODIFY,//修改联系人信息
	SHOW,//打印联系人信息
	SORT//排序联系人信息
};

int main()
{
	int input = 0;
	Contact con;//创建通讯录,不初始化里面的值是随机值,我们需要将大小初始化为0
	//初始化通讯录
	InitContact(&con);//形参不能修改实参,需要传地址
	do
	{
		menu();
		printf("请选择:>\n");
		scanf("%d", &input);
		switch (input)
		{
		case ADD:
			AddContact(&con);
			break;
		case DEL:
			DelContact(&con);
			break;
		case SEARCH:
			SearchContact(&con);
			break;
		case MODIFY:
			ModifyContact(&con);
			break;
		case SHOW:
			ShowContact(&con);
			break;
		case SORT:
			SortContact(&con);
			break;
		case EXIT:
			SaveContact(&con);//保存联系人信息
			DestroyContact(&con);//销毁通讯录
			printf("退出通讯录\n");
			break;
		default:
			printf("选择错误,请重新选择\n");
			break;
		}
	} while (input);
	return 0;
}

2.3、初始化通讯录

通讯录初始化有三个版本,一个是静态数组的版本,一个是动态数组的版本,一个是动态数组加文件的版本,因此操作可能有些许的差异,但是总体逻辑基本一致。

2.3.1、静态通讯录

使用内存设置函数将通讯录的内容都设置为0。

//静态版本
void InitContact(Contact* pc)
{
	assert(pc);//需要对数据进行解引用,加assert断言
	pc->sz = 0;//将联系人个数初始化为0
	memset(pc->data, 0, sizeof(pc->data));//将整个存放信息的数组初始化为0
}

2.3.2、动态通讯录

先开辟3个空间,将大小初始化为0,容量初始化为3。

#define DEFAULT_SZ 3//初始化空间大小
//动态版本
void InitContact(Contact* pc)
{
	assert(pc);//需要对数据进行解引用,加assert断言
	pc->sz = 0;//将联系人个数初始化为0
	pc->capacity = DEFAULT_SZ;
	pc->data = (PeoInfo*)calloc(pc->capacity, sizeof(PeoInfo));
	if (pc->data == NULL)
	{
		perror("InitContact malloc");
		return;
	}
}

2.3.3、动态文件通讯录

动态文件通讯录就是对动态通讯录不能保存数据进行优化,在使用通讯录之前先从文件中导入数据,再进行通讯录操作。

注意:文件版本因为需要导入数据,可能出现容量不够的情况,因此需要判断容量,因此需要在前面实现检查容量的函数。

检查容量函数:

#define DEFAULT_INC 2 //一次增减两个容量
void Check_Capacity(Contact* pc)
{
	if (pc->sz == pc->capacity)
	{
		PeoInfo* ptr = (PeoInfo*)realloc(pc->data, (pc->capacity + DEFAULT_INC) * sizeof(PeoInfo));
		if (ptr != NULL)
		{
			pc->data = ptr;
			pc->capacity += DEFAULT_INC;
			printf("增容成功\n");
		}
		else
		{
			perror("AddContact->realloc");
			return;
		}
	}
}

初始化函数: 

使用fread读取文件信息一次读取一个,如果返回值为0,说明文件信息读取完毕,退出循环即可。

void InitContact(Contact* pc)
{
	assert(pc);//需要对数据进行解引用,加assert断言
	pc->sz = 0;//将联系人个数初始化为0
	pc->capacity = DEFAULT_SZ;
	pc->data = (PeoInfo*)calloc(pc->capacity, sizeof(PeoInfo));
	if (pc->data == NULL)
	{
		perror("InitContact malloc");
		return;
	}
	//读取文件
	FILE* pf = fopen("contact.txt", "r");
	if (pf == NULL)
	{
		perror("InitContact");
		return;
	}
	PeoInfo tmp = { 0 };
	while (fread(&tmp, sizeof(PeoInfo), 1, pf))//每次读取一个大小,返回值为0则结束读取
	{
		Check_Capacity(pc);
		pc->data[pc->sz] = tmp;
		pc->sz++;
	}
}

2.4、添加联系人信息

添加联系人信息输入该联系人的相关信息将信息存放到通讯录中,但是在添加信息之前需要检查容量是否充足。 

void AddContact(Contact* pc)
{
	assert(pc);
	printf("添加联系人信息\n");
	//容量满了需要增容
	Check_Capacity(pc);
	printf("请输入名字:\n");
	scanf("%s", pc->data[pc->sz].name);
	printf("请输入年龄:\n");
	scanf("%d", &(pc->data[pc->sz].age));
	printf("请输入性别:\n");
	scanf("%s", pc->data[pc->sz].sex);
	printf("请输入电话:\n");
	scanf("%s", pc->data[pc->sz].tele);
	printf("请输入地址:\n");
	scanf("%s", pc->data[pc->sz].addr);

	pc->sz++;
	printf("增加成功\n");
}

2.5、打印联系人信息

为了测试添加联系人信息是否添加成功,因此先实现一个打印信息的函数,也为后面测试做铺垫。

为了打印出来的信息更加美观,博主在打印之前按照占位符对其数据进行打印,uu们也可以按照自己的喜好设置。

遍历整个通讯录,依次打印通讯录中的信息。

void ShowContact(const Contact* pc)
{
	assert(pc);
	printf("打印联系人信息\n");
	//判断是否有联系人
	if (pc->sz == 0)
	{
		printf("无联系人,无需打印\n");
		return;
	}
	printf("%-15s%-5s%-5s%-13s%-20s\n", "名字", "年龄", "性别", "电话", "地址");
	int i = 0;
	for (i = 0; i < pc->sz; i++)
	{
		printf("%-15s%-5d%-5s%-13s%-20s\n",
			pc->data[i].name, pc->data[i].age, pc->data[i].sex, pc->data[i].tele, pc->data[i].addr);
	}
}

2.6、删除联系人

博主实现的是通过名字搜索来查找删除的联系人,uu们可以按照自己的需要设计其他的查找函数喔,因为查找函数只需要在contact.c文件中使用,因此博主将查找函数加了static修饰,只能在该文件使用此函数。

按照名字查找函数:

找到则返回下标,没找到返回-1.

static int FindByName(const Contact* pc, char name[])//static修饰,只在该文件中使用
{
	int i = 0;
	for (i = 0; i < pc->sz; i++) 
	{
		if ((strcmp(pc->data[i].name, name)) == 0)
		{
			return i;
		}
	}
	return -1;//没有找到
}

删除联系人函数 

删除联系人信息之前判断通讯录中是否有数据,如果没有数据则给出提示,如果有数据再进行查找删除操作。

void DelContact(Contact* pc)
{
	char name[NAME_MAX];
	assert(pc);
	printf("删除联系人信息\n");
	if (pc->sz == 0)
	{
		printf("无联系人,无需删除\n");
		return;
	}
	//删除谁的信息,使用名字查找
	printf("请输入要删除联系人的名字\n");
	scanf("%s", name);
	int ret = FindByName(pc, name);
	if (ret == -1)
	{
		printf("没有该联系人,无需删除\n");
		return;
	}
	//删除信息 把后面一个人的信息赋值给前面一个人
	int i = 0;
	for (i = ret; i < pc->sz - 1; i++)
	{
		pc->data[i] = pc->data[i + 1];
	}
	pc->sz--;
	printf("删除成功\n");
}

2.7、 查找联系人

通过输入名字查找联系人没有找到则给出提示找到则打印该联系人的相关信息。(uu们也可以设计其他的查找方式喔!!!)

void SearchContact(const Contact* pc)
{
	char name[NAME_MAX];
	assert(pc);
	printf("查找联系人信息\n");
	printf("请输入要查找联系人的名字\n");
	scanf("%s", name);
	int ret = FindByName(pc, name);
	if (ret == -1)
	{
		printf("没有该联系人\n");
		return;
	}
	//打印该联系人信息
	printf("%-15s%-5s%-5s%-13s%-20s\n", "名字", "年龄", "性别", "电话", "地址");
	printf("%-15s%-5d%-5s%-13s%-20s\n",
		pc->data[ret].name, pc->data[ret].age, pc->data[ret].sex, pc->data[ret].tele, pc->data[ret].addr);
}

2.8、修改联系人

通过输入名字查找联系人没有找到则给出提示找到则修改该联系人的相关信息

void ModifyContact(Contact* pc)
{
	char name[NAME_MAX];
	assert(pc);
	printf("修改联系人信息\n");
	printf("请输入要修改联系人的名字\n");
	scanf("%s", name);
	int ret = FindByName(pc, name);
	if (ret == -1)
	{
		printf("没有该联系人\n");
		return;
	}
	printf("请输入名字:\n");
	scanf("%s", pc->data[ret].name);
	printf("请输入年龄:\n");
	scanf("%d", &(pc->data[ret].age));
	printf("请输入性别:\n");
	scanf("%s", pc->data[ret].sex);
	printf("请输入电话:\n");
	scanf("%s", pc->data[ret].tele);
	printf("请输入地址:\n");
	scanf("%s", pc->data[ret].addr);

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

2.9、排序联系人信息

排序方式函数:

此处实现的函数均为升序函数,包含所以成员变量的升序。

int cmp_by_name(const void* e1, const void* e2)
{
	return strcmp(((PeoInfo*)e1)->name, ((PeoInfo*)e2)->name);
}
int cmp_by_age(const void* e1, const void* e2)
{
	return ((PeoInfo*)e1)->age - ((PeoInfo*)e2)->age;
}
int cmp_by_sex(const void* e1, const void* e2)
{
	return strcmp(((PeoInfo*)e1)->sex, ((PeoInfo*)e2)->sex);
}
int cmp_by_tele(const void* e1, const void* e2)
{
	return strcmp(((PeoInfo*)e1)->tele, ((PeoInfo*)e2)->tele);
}
int cmp_by_addr(const void* e1, const void* e2)
{
	return strcmp(((PeoInfo*)e1)->addr, ((PeoInfo*)e2)->addr);
}

void SortByName(Contact* pc)
{
	qsort(pc->data, pc->sz, sizeof(PeoInfo), cmp_by_name);
}
void SortByAge(Contact* pc)
{
	qsort(pc->data, pc->sz, sizeof(PeoInfo), cmp_by_age);
}
void SortBySex(Contact* pc)
{
	qsort(pc->data, pc->sz, sizeof(PeoInfo), cmp_by_sex);
}
void SortByTele(Contact* pc)
{
	qsort(pc->data, pc->sz, sizeof(PeoInfo), cmp_by_tele);
}
void SortByAddr(Contact* pc)
{
	qsort(pc->data, pc->sz, sizeof(PeoInfo), cmp_by_addr);
}

排序联系人函数

输入想要进行排序的方式,输入错误会提示重新输入。

void SortContact(Contact* pc)
{
	assert(pc);
	char input[20];
	printf("排序联系人信息\n");
	printf("请选择如何进行排序,选择有名字,年龄,性别,电话,地址!\n");
	while (1)
	{
		scanf("%s", input);
		if (strcmp(input, "名字") == 0)
		{
			printf("按照姓名升序\n");
			SortByName(pc);
			ShowContact(pc);
			break;
		}
		else if (strcmp(input, "年龄") == 0)
		{
			printf("按照年龄升序\n");
			SortByAge(pc);
			ShowContact(pc);
			break;
		}
		else if (strcmp(input, "性别") == 0)
		{
			printf("按照性别升序\n");
			SortBySex(pc);
			ShowContact(pc);
		}
		else if (strcmp(input, "电话") == 0)
		{
			printf("按照电话升序\n");
			SortByTele(pc);
			ShowContact(pc);
			break;
		}
		else if (strcmp(input, "地址") == 0)
		{
			printf("按照地址升序\n");
			SortByTele(pc);
			ShowContact(pc);
			break;
		}
		else
		{
			printf("输入错误,请重新选择\n");
		}
	}
}

 2.10、存储联系人信息到文件

数据写入文件函数

使用fwrite函数依次将数据写入到文件中。

void SaveContact(Contact* pc)
{
	//将通讯录信息写入到文件中
	FILE* pf = fopen("contact.txt", "wb");
	if (pf == NULL)
	{
		perror("SaveContact");
		return;
	}
	//一个信息一个信息写入
	int i = 0;
	for (i = 0; i < pc->sz; i++)
	{
		//fwrite(&(pc->data[i]), sizeof(PeoInfo), 1, pf);
		fwrite(pc->data + i, sizeof(PeoInfo), 1, pf);
	}

	fclose(pf);
	pf = NULL;
}

2.11、销毁通讯录

动态版本通讯录才需要销毁通讯录。

释放动态开辟的内存空间,并将大小个容量初始化为0.

void DestroyContact(Contact* pc)
{
	free(pc->data);
	pc->data = NULL;
	pc->sz = 0;
	pc->capacity = 0;
}

3、完整代码

3.1、contact.h

#pragma once
#include<stdio.h>
#include<assert.h>
#include<string.h>
#include<stdlib.h>

//常量使用#define宏定义,便于以后修改
#define NAME_MAX 20
#define SEX_MAX 5
#define TELE_MAX 13
#define ADDR_MAX 30

#define MAX 100  //通讯录大小,存放多少人的信息

#define DEFAULT_SZ 3
#define DEFAULT_INC 2


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

//静态版本
//typedef struct Contact
//{
//	PeoInfo data[MAX];
//	int sz;
//}Contact;


typedef struct Contact
{
	PeoInfo* data;
	int sz;
	int capacity;
}Contact;


//初始化通讯录
void InitContact(Contact* pc);
//增加联系人
void AddContact(Contact* pc);
//显示所有联系人信息
void ShowContact(const Contact* pc);//不会修改值,可以加const
//删除指定联系人
void DelContact(Contact* pc);
//查找指定联系人
void SearchContact(const Contact* pc);
//修改指定联系人
void ModifyContact(Contact* pc);
//排序通讯录
void SortContact(Contact* pc);
//销毁通讯录
void DestroyContact(Contact* pc);
//将通讯录信息写入到文件中
void SaveContact(Contact* pc);

3.2、contact.c 

#define _CRT_SECURE_NO_WARNINGS 1

#include "contact.h"

//静态版本
//void InitContact(Contact* pc)
//{
//	assert(pc);//需要对数据进行解引用,加assert断言
//	pc->sz = 0;//将联系人个数初始化为0
//	memset(pc->data, 0, sizeof(pc->data));//将整个存放信息的数组初始化为0
//}

//动态版本
//void InitContact(Contact* pc)
//{
//	assert(pc);//需要对数据进行解引用,加assert断言
//	pc->sz = 0;//将联系人个数初始化为0
//	pc->capacity = DEFAULT_SZ;
//	pc->data = (PeoInfo*)calloc(pc->capacity, sizeof(PeoInfo));
//	if (pc->data == NULL)
//	{
//		perror("InitContact malloc");
//		return;
//	}
//}

void Check_Capacity(Contact* pc);

//文件版本
void InitContact(Contact* pc)
{
	assert(pc);//需要对数据进行解引用,加assert断言
	pc->sz = 0;//将联系人个数初始化为0
	pc->capacity = DEFAULT_SZ;
	pc->data = (PeoInfo*)calloc(pc->capacity, sizeof(PeoInfo));
	if (pc->data == NULL)
	{
		perror("InitContact malloc");
		return;
	}
	//读取文件
	FILE* pf = fopen("contact.txt", "r");
	if (pf == NULL)
	{
		perror("InitContact");
		return;
	}
	PeoInfo tmp = { 0 };
	while (fread(&tmp, sizeof(PeoInfo), 1, pf))//每次读取一个大小,返回值为0则结束读取
	{
		Check_Capacity(pc);
		pc->data[pc->sz] = tmp;
		pc->sz++;
	}
}

void DestroyContact(Contact* pc)
{
	free(pc->data);
	pc->data = NULL;
	pc->sz = 0;
	pc->capacity = 0;
}


//静态版本
//void AddContact(Contact* pc)
//{
//	assert(pc);
//	//判断通讯录是否满了
//	if (pc->sz == MAX)
//	{
//		printf("通讯录已满,不能添加联系人\n");
//		return;
//	}
//	printf("请输入名字:\n");
//	scanf("%s", pc->data[pc->sz].name);
//	printf("请输入年龄:\n");
//	scanf("%d", &(pc->data[pc->sz].age));
//	printf("请输入性别:\n");
//	scanf("%s", pc->data[pc->sz].sex);
//	printf("请输入电话:\n");
//	scanf("%s", pc->data[pc->sz].tele);
//	printf("请输入地址:\n");
//	scanf("%s", pc->data[pc->sz].addr);
//
//	pc->sz++;
//	printf("增加成功\n");
//}

void Check_Capacity(Contact* pc)
{
	if (pc->sz == pc->capacity)
	{
		PeoInfo* ptr = (PeoInfo*)realloc(pc->data, (pc->capacity + DEFAULT_INC) * sizeof(PeoInfo));
		if (ptr != NULL)
		{
			pc->data = ptr;
			pc->capacity += DEFAULT_INC;
			printf("增容成功\n");
		}
		else
		{
			perror("AddContact->realloc");
			return;
		}
	}
}
void AddContact(Contact* pc)
{
	assert(pc);
	printf("添加联系人信息\n");
	//容量满了需要增容
	Check_Capacity(pc);
	printf("请输入名字:\n");
	scanf("%s", pc->data[pc->sz].name);
	printf("请输入年龄:\n");
	scanf("%d", &(pc->data[pc->sz].age));
	printf("请输入性别:\n");
	scanf("%s", pc->data[pc->sz].sex);
	printf("请输入电话:\n");
	scanf("%s", pc->data[pc->sz].tele);
	printf("请输入地址:\n");
	scanf("%s", pc->data[pc->sz].addr);

	pc->sz++;
	printf("增加成功\n");
}

void ShowContact(const Contact* pc)
{
	assert(pc);
	printf("打印联系人信息\n");
	//判断是否有联系人
	if (pc->sz == 0)
	{
		printf("无联系人,无需打印\n");
		return;
	}
	printf("%-15s%-5s%-5s%-13s%-20s\n", "名字", "年龄", "性别", "电话", "地址");
	int i = 0;
	for (i = 0; i < pc->sz; i++)
	{
		printf("%-15s%-5d%-5s%-13s%-20s\n",
			pc->data[i].name, pc->data[i].age, pc->data[i].sex, pc->data[i].tele, pc->data[i].addr);
	}
}

static int FindByName(const Contact* pc, char name[])//static修饰,只在该文件中使用
{
	int i = 0;
	for (i = 0; i < pc->sz; i++) 
	{
		if ((strcmp(pc->data[i].name, name)) == 0)
		{
			return i;
		}
	}
	return -1;//没有找到
}
void DelContact(Contact* pc)
{
	char name[NAME_MAX];
	assert(pc);
	printf("删除联系人信息\n");
	if (pc->sz == 0)
	{
		printf("无联系人,无需删除\n");
		return;
	}
	//删除谁的信息,使用名字查找
	printf("请输入要删除联系人的名字\n");
	scanf("%s", name);
	int ret = FindByName(pc, name);
	if (ret == -1)
	{
		printf("没有该联系人,无需删除\n");
		return;
	}
	//删除信息 把后面一个人的信息赋值给前面一个人
	int i = 0;
	for (i = ret; i < pc->sz - 1; i++)
	{
		pc->data[i] = pc->data[i + 1];
	}
	pc->sz--;
	printf("删除成功\n");
}


void SearchContact(const Contact* pc)
{
	char name[NAME_MAX];
	assert(pc);
	printf("查找联系人信息\n");
	printf("请输入要查找联系人的名字\n");
	scanf("%s", name);
	int ret = FindByName(pc, name);
	if (ret == -1)
	{
		printf("没有该联系人\n");
		return;
	}
	//打印该联系人信息
	printf("%-15s%-5s%-5s%-13s%-20s\n", "名字", "年龄", "性别", "电话", "地址");
	printf("%-15s%-5d%-5s%-13s%-20s\n",
		pc->data[ret].name, pc->data[ret].age, pc->data[ret].sex, pc->data[ret].tele, pc->data[ret].addr);
}

void ModifyContact(Contact* pc)
{
	char name[NAME_MAX];
	assert(pc);
	printf("修改联系人信息\n");
	printf("请输入要修改联系人的名字\n");
	scanf("%s", name);
	int ret = FindByName(pc, name);
	if (ret == -1)
	{
		printf("没有该联系人\n");
		return;
	}
	printf("请输入名字:\n");
	scanf("%s", pc->data[ret].name);
	printf("请输入年龄:\n");
	scanf("%d", &(pc->data[ret].age));
	printf("请输入性别:\n");
	scanf("%s", pc->data[ret].sex);
	printf("请输入电话:\n");
	scanf("%s", pc->data[ret].tele);
	printf("请输入地址:\n");
	scanf("%s", pc->data[ret].addr);

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

int cmp_by_name(const void* e1, const void* e2)
{
	return strcmp(((PeoInfo*)e1)->name, ((PeoInfo*)e2)->name);
}
int cmp_by_age(const void* e1, const void* e2)
{
	return ((PeoInfo*)e1)->age - ((PeoInfo*)e2)->age;
}
int cmp_by_sex(const void* e1, const void* e2)
{
	return strcmp(((PeoInfo*)e1)->sex, ((PeoInfo*)e2)->sex);
}
int cmp_by_tele(const void* e1, const void* e2)
{
	return strcmp(((PeoInfo*)e1)->tele, ((PeoInfo*)e2)->tele);
}
int cmp_by_addr(const void* e1, const void* e2)
{
	return strcmp(((PeoInfo*)e1)->addr, ((PeoInfo*)e2)->addr);
}

void SortByName(Contact* pc)
{
	qsort(pc->data, pc->sz, sizeof(PeoInfo), cmp_by_name);
}
void SortByAge(Contact* pc)
{
	qsort(pc->data, pc->sz, sizeof(PeoInfo), cmp_by_age);
}
void SortBySex(Contact* pc)
{
	qsort(pc->data, pc->sz, sizeof(PeoInfo), cmp_by_sex);
}
void SortByTele(Contact* pc)
{
	qsort(pc->data, pc->sz, sizeof(PeoInfo), cmp_by_tele);
}
void SortByAddr(Contact* pc)
{
	qsort(pc->data, pc->sz, sizeof(PeoInfo), cmp_by_addr);
}

void SortContact(Contact* pc)
{
	assert(pc);
	char input[20];
	printf("排序联系人信息\n");
	printf("请选择如何进行排序,选择有名字,年龄,性别,电话,地址!\n");
	while (1)
	{
		scanf("%s", input);
		if (strcmp(input, "名字") == 0)
		{
			printf("按照姓名升序\n");
			SortByName(pc);
			ShowContact(pc);
			break;
		}
		else if (strcmp(input, "年龄") == 0)
		{
			printf("按照年龄升序\n");
			SortByAge(pc);
			ShowContact(pc);
			break;
		}
		else if (strcmp(input, "性别") == 0)
		{
			printf("按照性别升序\n");
			SortBySex(pc);
			ShowContact(pc);
		}
		else if (strcmp(input, "电话") == 0)
		{
			printf("按照电话升序\n");
			SortByTele(pc);
			ShowContact(pc);
			break;
		}
		else if (strcmp(input, "地址") == 0)
		{
			printf("按照地址升序\n");
			SortByTele(pc);
			ShowContact(pc);
			break;
		}
		else
		{
			printf("输入错误,请重新选择\n");
		}
	}
}

void SaveContact(Contact* pc)
{
	//将通讯录信息写入到文件中
	FILE* pf = fopen("contact.txt", "wb");
	if (pf == NULL)
	{
		perror("SaveContact");
		return;
	}
	//一个信息一个信息写入
	int i = 0;
	for (i = 0; i < pc->sz; i++)
	{
		//fwrite(&(pc->data[i]), sizeof(PeoInfo), 1, pf);
		fwrite(pc->data + i, sizeof(PeoInfo), 1, pf);
	}

	fclose(pf);
	pf = NULL;
}

3.3、test.c

#define _CRT_SECURE_NO_WARNINGS 1

#include "contact.h"
void menu()
{
	printf("********************************************\n");
	printf("**********   1.add      2.del     **********\n");
	printf("**********   3.search   4.modify  **********\n");
	printf("**********   5.show     6.sort    **********\n");
	printf("**********   0.exit               **********\n");
	printf("********************************************\n");
}

//增强代码可读性,使用枚举变量
enum Option
{
	EXIT,//退出游戏,默认值为0
	ADD,//添加联系人信息
	DEL,//删除联系人信息
	SEARCH,//查找联系人信息
	MODIFY,//修改联系人信息
	SHOW,//打印联系人信息
	SORT//排序联系人信息
};

int main()
{
	int input = 0;
	Contact con;//创建通讯录,不初始化里面的值是随机值,我们需要将大小初始化为0
	//初始化通讯录
	InitContact(&con);//形参不能修改实参,需要传地址
	do
	{
		menu();
		printf("请选择:>\n");
		scanf("%d", &input);
		switch (input)
		{
		case ADD:
			AddContact(&con);
			break;
		case DEL:
			DelContact(&con);
			break;
		case SEARCH:
			SearchContact(&con);
			break;
		case MODIFY:
			ModifyContact(&con);
			break;
		case SHOW:
			ShowContact(&con);
			break;
		case SORT:
			SortContact(&con);
			break;
		case EXIT:
			SaveContact(&con);
			DestroyContact(&con);
			printf("退出通讯录\n");
			break;
		default:
			printf("选择错误,请重新选择\n");
			break;
		}
	} while (input);
	return 0;
}

总结

本篇博客就结束啦,谢谢大家的观看,如果公主少年们有好的建议可以留言喔,谢谢大家啦!

  • 197
    点赞
  • 159
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 181
    评论
评论 181
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小林熬夜学编程

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

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

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

打赏作者

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

抵扣说明:

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

余额充值