实验课题——最全手机通信录实现版本(【含注释】848行代码)!!!(包括模糊查询、分类查找、模拟拨号、qsort函数实现排序、文件存储、防误触等功能)

目录

简介:

基本要求:

代码的实现:

1、Contact.h

2、test.c

3、Cantact.c

运行效果图:

部分复杂函数流程图


前两周是本人的实验周,抽到的课题是“手机通信录的实现”,课题大致如下:

简介:

(1)用C/C++设计出模拟手机通信录系统,实现对手机中的通信录进行管理。

(2)将通讯录用文件存储,人员信息包括:姓名、手机号码、家庭电话号码、办公电话、电子邮件、分组。

(3)其中:手机号码、家庭电话号码、办公电话不能同时为空;分组说明:未知、同事、亲戚、朋友、家人、同学等还可以自己创建分组

(4)文件类型可以是文本文件或二进制文件。

基本要求:

(1)首先向客户展示一个欢迎界面,并提醒客户输入任意键进入主菜单,在菜单中客户进行操作选择,而且客户操作完后还可以根据需求进行返回主菜单进行其他操作。

(2)增加功能:

能录入新人员记录。

(3)查看功能:
选择此功能时,当选中某类时,显示出此类所有数据中的姓名和电话号码。

(4)拔号功能:
能显示出通信录中所有人的姓名,当选中某个姓名时,屏幕上模拟打字机的效果依次显示出此人的电话号码中的各个数字。

(5)修改功能: .
选中某个人的姓名时,可对此人的相应数据进行修改。

我一看,嚯哟!这个课题好简单啊!除了“分类查找”和“拨号”是我没写过的,剩下的功能不都是最基础的吗?主要涉及到的知识点不就是些:数组应用、指针和结构体嘛!

可是转念一想,不行,写的那么简单,和别的组没有区分度,不方便老师打分呢~(bushi)

再说了,这个程序一天就可以写完,再算上写实验报告和画函数图解也最多不过一周的事,不是对不起这两周的实验周吗?(对不起,后来博主被狠狠打脸了。老师要求很严格,不仅被要求补充了一些代码,最后写实验报告的时候简直被折磨的不行,流程图重画了好几遍……实验报告一共也就写了那么整整15个小时吧)

于是我决定,要写就写个详细的通信录程序。不仅补充上了“分类查找”和“模拟拨号(类似打印机)”这两个功能,我还增加了一些别的小功能:

1、“模糊查询”(即你要找“Jackson”这个联系人的信息,输入“Jack”,通信录会跳出所以名字含这个字符串的联系人信息。对于中文名也是可以的,比如你想找“如花”,输入“如”字按回车,它会跳出一堆“如花”、“如玉”......);

2、qsort函数实现排序:可以选择按照姓名、年龄和电话号码排序。

3、防误触:听上去好像很厉害,其实很简单,只有三行代码就搞定了。(不过是让用户再次确认自己的选择避免造成不可挽回的损失罢了)。

总之,断断续续地,花了三天时间,我终于把这个小程序写完了。它现在一共有11个功能,博主暂时想不到别的了,大家如果有好的想法可以提出来。

其实它还不是特别完备,只是一个静态的版本,我打算有时间的话把它改造成一个动态的版本,这就要涉及到我们的malloc、calloc 、free 和 realloc 函数了。

扯远了,现在让我们来看看这个通信录吧:

代码的实现:

1、Contact.h

宏定义的设置可以方便我们以后的修改。

#pragma once
#include
#include
#include
#include
#include
#define MAX_NAME 10
#define MAX_SEX 5
#define MAX_HOME_NUM 12
#define MAX_NUM 12
#define MAX_OFFICE 15
#define MAX_DATA 100
#define MAX_E_MAIL 20
#define MAX_CLASSIFICATION 10
//创建联系人结构体
struct Peoinfo
{
	char name[MAX_NAME];	
	char sex[MAX_SEX];
	char home_num[MAX_HOME_NUM];
	char cell_num[MAX_NUM];
	int age;
	char office_number[MAX_OFFICE];
	char e_mail[MAX_E_MAIL];
	char classification[MAX_CLASSIFICATION];
};

//创建通讯录结构体
struct Contact
{
	struct Peoinfo data[MAX_DATA];//存放数据
	int sz;//记录通讯录中的有效信息个数
	//int capcity;
};

//把上次存在文件中的信息加载过来
void LoadContact(struct Contact* pc);
//初始化通讯录
void init_contact(struct Contact* con);
//添加联系人
void addContact(struct Contact* con);
//删除联系人
void deleteContact(struct Contact* con);
//修改联系人
void modifyContact(struct Contact* con);
//寻找联系人
static int find(const struct Contact* con, char* name);
//查找联系人
void searchContact(struct Contact* con);
//显示联系人信息
void showContact(struct Contact* con);
//排序联系人(按照年龄、名字)
void sortContact(struct Contact con);
//清空联系人
void clearContact(struct Contact* con);
//查看某一类联系人
void checkContact(struct Contact* con);
//保存联系人
void saveContact(struct Contact* con);
//拨号
void dialContact(struct Contact* con);
//把上次存在文件中的信息加载过来
//void LoadContact(struct Contact* pc);
//模糊查询联系人信息
void fuzzy_search(struct Contact* contact);

2、test.c

这里想要解释一下为什么要加一个枚举常量。(后面的Contact.c也添加了一个枚举,目的是一样的。)

枚举的使用增加了我们代码的可读性和维护性:

有时候在写一些代码时,比如写这个菜单,我们用了1来代表add,0代表exit,但是在后续代码中,1和0的真实含义有时难以被区分。所以如果用枚举的话,后续在case 后面加常量的时候就可以直接写add,这样,代码的可读性就增强了不少)。

注意!我们的防误触操作就设置在此处,因为它实在是太简单了,没有单独为它写一个函数的必要呢。

(和大家讲个笑话,活跃一下气氛。在界面设计这里呢,本来博主有一点自己的小私心,想要提醒老师我有模糊查询这个功能,让她夸夸我来着,但是她好像并不care,反而对我层层嵌套调用函数的addContact子功能青睐有加。)

#define _CRT_SECURE_NO_WARNINGS

#include "contact.h"
void Meue()
{
	printf("***亲爱的用户,欢迎您使用通信录!***\n");
	printf("***温馨提示,本通信录支持模糊查询的功能哦!***\n");
	printf("************************************\n");
	printf("*****   1.add       2.delete   *****\n");
	printf("*****   3.modify    4.search   *****\n");
	printf("*****   5.show      6.sort     *****\n");
	printf("*****   7.clear     8.check    *****\n");
	printf("*****   9.save      10.dial    *****\n");
    printf("*****   11.fuzzy_search        *****\n");	
	printf("*****   0.exit                 *****\n");
	printf("************************************\n");
}
int main()
{
	int input = 0;
	enum number
	{
		EXIT,//0
		ADD,//1
	    DELE,//2
		MODIFY,
		SEARCH,
		SHOW,
		SORT,
		CLEAR,
		CHECK,
		SAVE,
		DIAL,
		FUZZY_SEARCH,
	};
	struct Contact con;//通信录
	init_contact(&con);//初始化通信录
	LoadContact(&con);//把上次存在文件中的信息加载过来
	do
	{
		Meue();
		printf("请选择:");
		scanf_s("%d", &input);
		switch (input)
		{
		case EXIT:
			//这里设置一个防误触的功能,提醒用户再次确认是否要保存一下联系人信息再退出
			printf("请问需要保存一下通讯录的信息再退出吗?\n");
			printf("**********   1.保存一下   0.直接退出  **********\n");
			int q = 0;
			scanf("%d", &q);
			if (q==0)
			{
				printf("退出通信录!\n");
				break;
			} 
			saveContact(&con);
			printf("退出通信录!\n");
			break;
		case ADD:
			addContact(&con);
			break;
		case DELE:
			deleteContact(&con);
			break;
		case MODIFY:
			modifyContact(&con);
			break;
		case SEARCH:
			searchContact(&con);
			break;
		case SHOW:
			showContact(&con);
			break;
		case SORT:
			sortContact(con);
			break;
		case CLEAR:
			//这里设置一个防误触的功能,提醒用户再次确认是否要清空联系人信息
			printf("请问您确定要清空联系人信息吗?一旦清空,无法找回!\n");
			printf("*************   1.确定清空   0.取消  *************\n");
			int x = 0;
			scanf("%d",&x);
			if (x==1) 
			{
				clearContact(&con);
			}
			break;
		case CHECK:
			checkContact(&con);
			break;
		case SAVE:
			saveContact(&con);
			break;
		case DIAL:
			dialContact(&con);
			break;
		case FUZZY_SEARCH:
			fuzzy_search(&con);
			break;
		default:
			printf("输入错误!\n");
			break;
		}
	} while (input);
	return 0;
}

3、Cantact.c

这个源文件定义了每一个子函数的实现,还添加了两个小的 find 函数帮助个别子函数的实现。

注解都写了,大家可以好好看一下:

#define _CRT_SECURE_NO_WARNINGS
#include "contact.h"
//把上次存在文件中的信息加载过来
void LoadContact(struct Contact* pc)
{
	//打开文件
	FILE* pf = fopen("D:\\C语言2\\手机通信录实现\\contact.dat", "rb");
	if (pf == NULL)
	{
		perror("LoadContact::fopen");
		return;
	}
	//读文件
	struct Peoinfo tmp = { 0 };
	while (fread(&tmp, sizeof(struct Peoinfo), 1, pf))
	{
		pc->data[pc->sz] = tmp;
		pc->sz++;
	}

	//关闭文件
	fclose(pf);
	pf = NULL;
}
//初始化通讯录
void init_contact(struct Contact* pc)
{
	assert(pc);
	pc->sz = 0;
	memset(pc->data, 0, MAX_DATA * sizeof(struct Peoinfo));
}
//add中判断是否存在相同姓名联系人
int repetition_name(struct Contact * con, char* len)
{
	int ret = find(con, len);
	int i = 0;
	if (ret == -1)
		return 3;
	else
	{
		int repe;
		printf("已存在同名联系人,请选择:>\n");
		printf("*** 0.重新输入 1.覆盖  2.新建 ***\n");
		scanf("%d", &repe);
		switch (repe)
		{
		case 0:
			return 0;
		case 1:
			i = 0;
			for (i = 0; i < con->sz - ret; i++)
			{
				con->data[ret + i] = con->data[ret + i + 1];
			}
			con->sz--;
			printf("覆盖联系人成功!\n");
			return 1;
		case 2:
			return 2;
		}
	}
}
//add中判断是否存在相同号码联系人
int repetition_num(struct Contact* con, char* len)
{
	int ret = findnum(con, len);
	int i = 0;
	if (ret == -1)
		return 2;
	else
	{
		int repe;
		printf("已存在相同号码的联系人,请选择:>\n");
		printf("*** 0.重新输入 1.覆盖  ***\n");//号码是无法相同的,所以不要新建
		scanf("%d", &repe);
		switch (repe)
		{
		case 0:
			return 0;
		case 1:
			i = 0;
			for (i = 0; i < con->sz - ret; i++)
			{
				con->data[ret + i] = con->data[ret + i + 1];
			}
			con->sz--;
			printf("覆盖联系人成功!\n");
			return 1;
		}
	}
}
//add中用于判断电话号码中是否存在非数字字符
int isnumber(const char* ps)
{
	while (*ps != '\0')
	{
		if (*ps < '0' || *ps > '9')
		{
			return 0;
		}
		ps++;
	}
	if (*ps == '\0')
	{
		return 1;
	}
}
//新建联系人
void new(struct Contact* con, char* len)
{
	char new[MAX_NAME] = {0};
    strcpy(new, con->data[con->sz].name);
}
//添加联系人
void addContact(struct Contact* con)
{
	assert(con);
	int ret;
	int k ;
	char new[MAX_NAME] = {0};
	if ((con->sz) == MAX_DATA)
	{
		printf("通讯录已满!无法增加\n");
		return;
	}
	printf("请输入名字:>");	
	do
	{
		scanf("%s", con->data[con->sz].name);//数组名本身就是首元素地址
		k = repetition_name(con, con->data[con->sz].name);
		if(k==0)
			printf("请重新输入!\n");
		if (k == 2)
		{
			printf("请输入新的备注:>");
			scanf("%s",new );
			strcat(con->data[con->sz].name,new);//在名字后添加备注
		}
	} while (!k);

	printf("请输入性别:>");
	scanf("%s", con->data[con->sz].sex);
	printf("请输入家庭电话:>");
	//判断是否有非数字字符
	do
	{
		ret = 1;
		scanf("%s", con->data[con->sz].home_num);
		if (!isnumber(con->data[con->sz].home_num))
			printf("提示!输入的号码中存在非数字字符,请重新输入!\n");
		else
			ret = 0;
	} while (ret);
	//因为是家庭号码,所以此处不需要判断是否已有相同号码联系人存在

	printf("请输入个人电话:>");
	do
	{
		ret = 1;
		scanf("%s", con->data[con->sz].cell_num);
		if (!isnumber(con->data[con->sz].cell_num))
			printf("提示!输入的号码中存在非数字字符,请重新输入!\n");
		else
			ret = 0;
	} while (ret);
	//判断是否已有相同号码联系人存在
	do
	{
		k = 1;
		k = repetition_num(con, con->data[con->sz].cell_num);
		if (!k)
		{ 
			printf("请重新输入!\n");
			scanf("%s", con->data[con->sz].cell_num);
			k = 1;
		}
		else
			k = 0;
	} while (k);

	printf("请输入年龄:>");
	scanf("%d", &(con->data[con->sz].age));//这里需要取地址
	printf("请输入办公电话:>");
	do
	{
		ret = 1;
		scanf("%s", con->data[con->sz].office_number);
		if (!isnumber(con->data[con->sz].office_number))
			printf("提示!输入的号码中存在非数字字符,请重新输入!\n");
		else
			ret = 0;
	} while (ret);
	//因为是办公号码,所以此处不需要判断是否已有相同号码联系人存在 

	printf("请输入邮箱:>");
	scanf("%s", con->data[con->sz].e_mail);
	printf("请输入分类:>\n");
	printf("您可以在以下选项中进行选择并输入:>\n");
	printf("同事  亲戚  朋友  家人  同学  老师  其他\n");
	scanf("%s", con->data[con->sz].classification);
	con->sz++;
	printf("添加联系人成功!\n");
}
//删除联系人
void deleteContact(struct Contact* con)
{
	if (con->sz == 0)
	{
		printf("通讯录为空,无法删除!\n");
		return;
	}
	char name[MAX_NAME];
	printf("请输入要删除的联系人:");
	scanf("%s", name);
	int ret = find(con, name);
	if (ret == -1)
		printf("未找到该联系人!\n");
	else
	{
		int i = 0;
		for (i = 0; i < con->sz - ret; i++)
		{
			con->data[ret + i] = con->data[ret + i + 1];
		}
		con->sz--;
		printf("删除联系人成功!\n");
	}
}
//修改联系人
void modifyContact(struct Contact* con)
{
	char name[MAX_NAME];
	printf("请输入要修改的联系人:");
	scanf("%s", name);
	int ret = find(con, name);
	if (ret == -1)
		printf("未找到该联系人!\n");
	else
	{
		int n = 0;
		do
		{
			int ret;
			int k;
			printf("请选择你想要修改的信息:\n");
			printf("%-10s\t%-5s\t%-12s\t%-12s\t%-5s\t%-15s\t%-20s\t%-10s\n",
				"0.退出修改","1.姓名", "2.性别", "3.家庭电话", "4.个人电话", "5.年龄", "6.办公电话", "7.邮箱", "8.分类");
			scanf("%d", &n);
			switch (n)
			{
			case NAME:
				printf("请输入你想修改的姓名:");
				do
				{
					scanf("%s", con->data[con->sz].name);//数组名本身就是首元素地址
					k = repetition_name(con, con->data[con->sz].name);
					if (k == 0)
						printf("请重新输入!\n");
					if (k == 2)
					{
						printf("请输入新的备注:>");
						scanf("%s", new);
						strcat(con->data[con->sz].name, new);//在名字后添加备注
					}
				} while (!k);

				printf("修改成功!\n");
				break;
			case SEX:
				printf("请输入你想修改的性别:");
				scanf("%s", con->data[ret].sex);
				break;
			case HOME_NUM:
				printf("请输入你想修改的家庭电话:");
				//判断是否有非数字字符
				do
				{
					ret = 1;
					scanf("%s", con->data[con->sz].home_num);
					if (!isnumber(con->data[con->sz].home_num))
						printf("提示!输入的号码中存在非数字字符,请重新输入!\n");
					else
						ret = 0;
				} while (ret);
				//因为是家庭号码,所以此处不需要判断是否已有相同号码联系人存在
				break;
			case CELL_NUM:
				printf("请输入你想修改的手机号码:");
				do
				{
					ret = 1;
					scanf("%s", con->data[con->sz].cell_num);
					if (!isnumber(con->data[con->sz].cell_num))
						printf("提示!输入的号码中存在非数字字符,请重新输入!\n");
					else
						ret = 0;
				} while (ret);
				//判断是否已有相同号码联系人存在
				do
				{
					k = 1;
					k = repetition_num(con, con->data[con->sz].cell_num);
					if (!k)
					{
						printf("请重新输入!\n");
						scanf("%s", con->data[con->sz].cell_num);
						k = 1;
					}
					else
						k = 0;
				} while (k);

				break;
			case AGE:
				printf("请输入你想修改的年龄:");
				scanf("%d", &con->data[ret].age);//不是数组,需要写地址符
				break;
			case OFFICE_NUMBER:
				printf("请输入你想修改的办公电话:");
				do
				{
					ret = 1;
					scanf("%s", con->data[con->sz].office_number);
					if (!isnumber(con->data[con->sz].office_number))
						printf("提示!输入的号码中存在非数字字符,请重新输入!\n");
					else
						ret = 0;
				} while (ret);
				//因为是办公号码,所以此处不需要判断是否已有相同号码联系人存在 
				break;
			case E_MAIL:
				printf("请输入你想修改的电子邮箱:");
				scanf("%s", con->data[ret].e_mail);
				break;
			case CLASSFICATION:
				printf("请输入你想修改的分组:");
				scanf("%s", con->data[ret].classification);
				break;
			case LEAVE:
				printf("不修改退回页面\n");
				break;
			default:
				printf("无效操作数!\n");
			}
		} while (n);
	}
}
    
    //下面这些代码是表示每次修改都要重新输入该联系人的所有信息的,
    //因为不太方便,所以改造了一下,
    //改成上面那样,只用修改自己想要修改的信息

    //else
    //{
	//	printf("请输入姓名:");
	//	scanf("%s", con->data[ret].name);
	//	printf("请输入性别:");
	//	scanf("%s", con->data[ret].sex);
	//	printf("请输入电话:");
	//	scanf("%s", con->data[ret].home_num);
	//	printf("请输入电话:");
	//	scanf("%s", con->data[ret].cell_num);
	//	printf("请输入年龄:");
	//	scanf("%d", &(con->data[ret].age));
	//	printf("请输入办公电话:");
	//	scanf("%s", con->data[ret].office_number);
	//	printf("请输入邮箱:");
	//	scanf("%s", con->data[ret].e_mail);
	//	printf("请输入分类:");
	//	scanf("%s", con->data[ret].classification);
	//	printf("修改联系人成功!\n");
    //}



//寻找联系人是否存在——因为有多个功能都需要我们先去确认是否存在联系人,
//所以干脆把这个功能作为一个函数独立出去
static int find(const struct Contact* con, char* name)
//加上static之后就不会被别人看到,只能在该源文件使用
{
	int i = 0;
	for (i = 0; i < con->sz; i++)
	{
		if (0 == strcmp(con->data[i].name, name))
			return i;
	}
	return -1;//i==con->sz
}
static int findnum(const struct Contact* con, char* number)
//同样的,查找联系人电话我们也把它独立出来
{
	int i = 0;
	for (i = 0; i < con->sz; i++)
	{
		if (0 == strcmp(con->data[i].cell_num, number))
			return i;
	}
	return -1;//i==con->sz
}
//查找联系人
void searchContact(struct Contact* con)
{
	assert(con);
	char name[20] = { 0 };
	char number[12] = {0};
	int n;
	printf("请问您想要通过那种方式查找联系人:\n");
	printf("***    1.姓名    2.电话号码    ***\n");
	int ret=0;
	scanf("%d",&n);
	switch (n)
	{
	    case 1:
			printf("请输入要查找的联系人姓名:");
			scanf("%s", &name);
			ret = find(con, name);
			if (ret == -1)
				printf("未找到该联系人!\n");
			else
			{
				printf("%-10s\t%-5s\t%-12s\t%-12s\t%-5s\t%-15s\t%-20s\t%-10s\n",
					"姓名", "性别","家庭电话", "个人电话", "年龄", "办公电话", "邮箱", "分类");
				printf("%-10s\t%-5s\t%-12s\t%-12s\t%-5d\t%-15s\t%-20s\t%-10s\n",
					con->data[ret].name, con->data[ret].sex, con->data[ret].home_num,
					con->data[ret].cell_num, con->data[ret].age, con->data[ret].office_number,
					con->data[ret].e_mail, con->data[ret].classification);
			}
			break;
		case 2:
			printf("请输入要查找的联系人的电话号码:");
			scanf("%s", &number);
			ret = findnum(con, number);
			if (ret == -1)
				printf("未找到该联系人!\n");
			else
			{
				printf("%-10s\t%-5s\t%-12s\t%-12s\t%-5s\t%-15s\t%-20s\t%-10s\n",
					"姓名","性别", "家庭电话", "个人电话", "年龄", "办公电话", "邮箱", "分类");
				printf("%-10s\t%-5s\t%-12s\t%-12s\t%-5d\t%-15s\t%-20s\t%-10s\n",
					con->data[ret].name, con->data[ret].sex, con->data[ret].home_num,
					con->data[ret].cell_num, con->data[ret].age, con->data[ret].office_number,
					con->data[ret].e_mail, con->data[ret].classification);
			}
			break;

	}
	
}
//显示联系人
void showContact(struct Contact* con)
{
	assert(con);
	int i = 0;
	//姓名      性别   家庭电话      个人电话           年龄          办公电话      邮箱                  分类
	//zhangsan  男     1245676789    13245123123        20            12538912332   [email protected]   家人
	printf("%-10s\t%-5s\t%-12s\t%-12s\t%-5s\t%-15s\t%-20s\t%-10s\n",
		"姓名", "性别","家庭电话", "个人电话", "年龄", "办公电话", "邮箱", "分类");
	for (i = 0; i < con->sz; i++)
	{
		printf("%-10s\t%-5s\t%-12s\t%-12s\t%-5d\t%-15s\t%-20s\t%-10s\n",
			con->data[i].name, con->data[i].sex, con->data[i].home_num,
			con->data[i].cell_num, con->data[i].age, con->data[i].office_number,
			con->data[i].e_mail, con->data[i].classification);
	}

}
//按姓名排序
int name_cmp(const void* e1, const void* e2)
{
	return strcmp(((struct Peoinfo*)e1)->name, ((struct Peoinfo*)e2)->name);
}
//按办公电话排序
int office_number_cmp(const void* e1, const void* e2)
{
	return strcmp(((struct Peoinfo*)e1)->office_number,((struct Peoinfo*)e2)->office_number);
}
//按年龄排序
int age_cmp(const void* e1, const void* e2)
{
	return ((struct Peoinfo*)e1)->age - ((struct Peoinfo*)e2)->age;
}
//排序通讯录
void sortContact(struct Contact con)
{
	printf("********* 请选择排序方式:**********\n");
	printf("********* 1.姓名         ***********\n");
	printf("********* 2.办公电话     ***********\n");
	printf("********* 3.年龄         ***********\n");
	printf("************************************\n");
	int a = 0;
	scanf("%d", &a);
	switch (a)
	{
	case 1:
		qsort(con.data, con.sz, sizeof(con.data[0]), name_cmp);
		break;
	case 2:
		qsort(con.data, con.sz, sizeof(con.data[0]), office_number_cmp);
		break;
	case 3:
		qsort(con.data, con.sz, sizeof(con.data[0]), age_cmp);
		break;
	default:
		printf("输入错误\n");
		break;
	}
	int i = 0;
	printf("%-10s\t%-5s\t%-12s\t%-12s\t%-5s\t%-15s\t%-20s\t%-10s\n",
		"姓名","性别", "家庭电话", "个人电话", "年龄", "办公电话", "邮箱", "分类");
	for (i = 0; i < con.sz; i++)
	{
		printf("%-10s\t%-5s\t%-12s\t%-12s\t%-5d\t%-15s\t%-20s\t%-10s\n",
			con.data[i].name, con.data[i].sex, con.data[i].home_num,
			con.data[i].cell_num, con.data[i].age, con.data[i].office_number,
			con.data[i].e_mail, con.data[i].classification);
	}
}
//清空联系人
void clearContact(struct Contact* con)
{
	con->sz = 0;
	printf("清空联系人成功!\n");
}

//查看某一类联系人
void checkContact(struct Contact* con)
{
	char classification[10];
	int jude = 0;
	do 
	{
		printf("人员信息如下:\n");
		printf("同事  亲戚  朋友  家人  同学  老师  其他\n");
		printf("请输入查看人员的类别:\n");
		scanf("%s", classification);
		//用来判断输入字符串是否符合分类名称
		char* judgeclass[7] = { "同事",  "亲戚", "朋友","家人","同学","老师","其他" };
		for (jude = 0; jude < 7; jude++)
		{
			if ((strcmp(judgeclass[jude], classification) == 0))
				break;
		}
		if (jude == 7)
		{
			printf("您输入的分类名错误,请重新输入!>\n");
		}
	} while (jude==7);
	for(int ssz= con->sz - 1;ssz>=0;ssz--)
	{
		if (strcmp((con->data[ssz].classification), classification) == 0)
		{
			printf("%-10s\t%-12s\t%-12s\t%-15s\n", "姓名", "家庭电话", "个人电话", "办公电话");
			printf("%-10s\t%-12s\t%-12s\t%-15s\n",
				con->data[ssz].name, con->data[ssz].home_num,
				con->data[ssz].cell_num, con->data[ssz].office_number);

		}
	}
		printf("查找结束!\n");
}
//保存联系人
void saveContact(struct Contact* con)
{
		FILE* pf = fopen("D:\\C语言2\\手机通信录实现\\contact.dat", "wb");
		if (pf == NULL)
		{
			//显示错误信息,表示是在saveContact函数内的这个地方出错了!
			perror("SaveContact::fopen");
			return;
		}
		//写数据
		int i = 0;
		for (i = 0; i < con->sz; i++)
		{
			//&(con->data[i])可以写成con->data+i,更简单
			fwrite(con->data + i, sizeof(struct Peoinfo), 1, pf);
		}
		//关闭文件
		fclose(pf);
		pf = NULL;
		//简单提示一下
		printf("保存成功!\n");
}

//拨号功能
void dialContact(struct Contact* con)
{
	if (con->sz == 0)
	{
		printf("通信录中没有联系人信息!\n");
		return;
	}

	printf("通讯录中所有联系人如下:\n");
	for (int i = 0; i < con->sz; i++)
	{
		printf("%d . 姓名:%s\n", i + 1, con->data[i].name);
	}
	int choice = 0;
	printf("请选择您要拨打电话的联系人编号(输入0返回主菜单):");
	scanf("%d", &choice);
	if (choice < 0 || choice > con->sz)
	{
		printf("输入的编号有误,请重新选择!\n");
		dialContact(con);//返回拨号函数选择处
		return;
	}
	if (choice == 0)
	{
		return;
	}
	printf("您选择的联系人是:%s\n", con->data[choice - 1].name);
	printf("个人电话:");
	// 模拟打字机效果逐个显示电话号码中的数字
	for (int i = 0; i < strlen(con->data[choice - 1].cell_num); i++)
	{
		printf("%c", con->data[choice - 1].cell_num[i]);
		Sleep(300);  // 每个数字之间停顿一段时间
	}
	printf("\n");
	printf("家庭电话:");
	// 模拟打字机效果逐个显示电话号码中的数字
	for (int i = 0; i < strlen(con->data[choice - 1].home_num); i++)
	{
		printf("%c", con->data[choice - 1].home_num[i]);
		Sleep(300);  // 每个数字之间停顿一段时间
	}
	printf("\n");
	printf("办公电话:");
	// 模拟打字机效果逐个显示电话号码中的数字
	for (int i = 0; i < strlen(con->data[choice - 1].office_number); i++)
	{
		printf("%c", con->data[choice - 1].office_number[i]);
		Sleep(300);  // 每个数字之间停顿一段时间
	}
	printf("\n");
}

// 子函数,实现模糊查找功能
void fuzzy_search(struct Contact* contact)
{
	assert(contact);
	char fuzzy_name[10] = {0};
	printf("请输入想要查询的联系人的姓名:\n");
	scanf("%s",fuzzy_name);
	char* key = fuzzy_name;
	// 检查通讯录中是否有联系人信息,如果没有,无法进行模糊查询
	if (contact->sz == 0)
	{
		printf("通讯录中没有联系人信息!\n");
		return;
	}

	int index[MAX_DATA];
	int count = 0;

	// 遍历通讯录中的每个联系人
	for (int i = 0; i < contact->sz; i++)
	{
		struct Peoinfo* person = &contact->data[i];
		// 在联系人的各个字段中查找是否包含要查找的字符串key
		if (strstr(person->name, key))
		{
			printf("%-10s\t%-5s\t%-12s\t%-12s\t%-5s\t%-15s\t%-20s\t%-10s\n",
				"姓名", "性别", "家庭电话", "个人电话", "年龄", "办公电话", "邮箱", "分类");
			printf("%-10s\t%-5s\t%-12s\t%-12s\t%-5d\t%-15s\t%-20s\t%-10s\n",
				person->name, person->sex, person->home_num, person->cell_num, person->age, person->office_number,
				person->e_mail, person->classification);
			index[count++] = i;
		}
	}
		// 如果没有找到匹配的联系人,则输出提示信息
		if (count == 0)
		{
			printf("未找到匹配的联系人!\n");
		}
		else
		{
			printf("共找到 %d 个匹配的联系人,分别为:", count);
			for (int i = 0; i < count; i++)
			{
				printf("第%d个联系人 ", index[i]+1);
			    //让联系人看起来是从第1个开始排序的,即下标0处跳过
			}
			printf("\n");
		}
}

运行效果图:

1、增加联系人

实验课题——最全手机通信录实现版本(【含注释】848行代码)!!!(包括模糊查询、分类查找、模拟拨号、qsort函数实现排序、文件存储、防误触等功能)_第1张图片

 总的显示给大家看一下:

实验课题——最全手机通信录实现版本(【含注释】848行代码)!!!(包括模糊查询、分类查找、模拟拨号、qsort函数实现排序、文件存储、防误触等功能)_第2张图片

 现在假设我们重新输入,来看看如果出现名字相同的联系人,程序会有什么反应吧:

实验课题——最全手机通信录实现版本(【含注释】848行代码)!!!(包括模糊查询、分类查找、模拟拨号、qsort函数实现排序、文件存储、防误触等功能)_第3张图片

 实验课题——最全手机通信录实现版本(【含注释】848行代码)!!!(包括模糊查询、分类查找、模拟拨号、qsort函数实现排序、文件存储、防误触等功能)_第4张图片

 实验课题——最全手机通信录实现版本(【含注释】848行代码)!!!(包括模糊查询、分类查找、模拟拨号、qsort函数实现排序、文件存储、防误触等功能)_第5张图片

 实验课题——最全手机通信录实现版本(【含注释】848行代码)!!!(包括模糊查询、分类查找、模拟拨号、qsort函数实现排序、文件存储、防误触等功能)_第6张图片

来看看新建同名联系人后的小米和小米(1)吧: 实验课题——最全手机通信录实现版本(【含注释】848行代码)!!!(包括模糊查询、分类查找、模拟拨号、qsort函数实现排序、文件存储、防误触等功能)_第7张图片

 那电话号码相同的呢?可以新建吗?当然不行,和姓名相同时的代码略有不同:实验课题——最全手机通信录实现版本(【含注释】848行代码)!!!(包括模糊查询、分类查找、模拟拨号、qsort函数实现排序、文件存储、防误触等功能)_第8张图片

 如果选择覆盖呢?实验课题——最全手机通信录实现版本(【含注释】848行代码)!!!(包括模糊查询、分类查找、模拟拨号、qsort函数实现排序、文件存储、防误触等功能)_第9张图片

 小米不见了呢!(对不起,小米):实验课题——最全手机通信录实现版本(【含注释】848行代码)!!!(包括模糊查询、分类查找、模拟拨号、qsort函数实现排序、文件存储、防误触等功能)_第10张图片

 当然,我们迷人的addContact函数还有一个功能,就是会判断你输入的三个电话号码中是否存在非数字字符,来看一下吧:实验课题——最全手机通信录实现版本(【含注释】848行代码)!!!(包括模糊查询、分类查找、模拟拨号、qsort函数实现排序、文件存储、防误触等功能)_第11张图片

2、删除联系人

实验课题——最全手机通信录实现版本(【含注释】848行代码)!!!(包括模糊查询、分类查找、模拟拨号、qsort函数实现排序、文件存储、防误触等功能)_第12张图片

 总的显示给大家看一下:实验课题——最全手机通信录实现版本(【含注释】848行代码)!!!(包括模糊查询、分类查找、模拟拨号、qsort函数实现排序、文件存储、防误触等功能)_第13张图片

 3、修改联系人信息:

实验课题——最全手机通信录实现版本(【含注释】848行代码)!!!(包括模糊查询、分类查找、模拟拨号、qsort函数实现排序、文件存储、防误触等功能)_第14张图片

  总的显示给大家看一下:实验课题——最全手机通信录实现版本(【含注释】848行代码)!!!(包括模糊查询、分类查找、模拟拨号、qsort函数实现排序、文件存储、防误触等功能)_第15张图片

4、寻找单个联系人 实验课题——最全手机通信录实现版本(【含注释】848行代码)!!!(包括模糊查询、分类查找、模拟拨号、qsort函数实现排序、文件存储、防误触等功能)_第16张图片

 5、显示整个通讯录(刚刚已经展示过了)

6、排序

实验课题——最全手机通信录实现版本(【含注释】848行代码)!!!(包括模糊查询、分类查找、模拟拨号、qsort函数实现排序、文件存储、防误触等功能)_第17张图片

 实验课题——最全手机通信录实现版本(【含注释】848行代码)!!!(包括模糊查询、分类查找、模拟拨号、qsort函数实现排序、文件存储、防误触等功能)_第18张图片

 实验课题——最全手机通信录实现版本(【含注释】848行代码)!!!(包括模糊查询、分类查找、模拟拨号、qsort函数实现排序、文件存储、防误触等功能)_第19张图片

7、清除所有联系人实验课题——最全手机通信录实现版本(【含注释】848行代码)!!!(包括模糊查询、分类查找、模拟拨号、qsort函数实现排序、文件存储、防误触等功能)_第20张图片

 8、按分类查找某一类的联系人 

实验课题——最全手机通信录实现版本(【含注释】848行代码)!!!(包括模糊查询、分类查找、模拟拨号、qsort函数实现排序、文件存储、防误触等功能)_第21张图片

 9、保存到文件里

实验课题——最全手机通信录实现版本(【含注释】848行代码)!!!(包括模糊查询、分类查找、模拟拨号、qsort函数实现排序、文件存储、防误触等功能)_第22张图片

 已经保存到当前目录下了:实验课题——最全手机通信录实现版本(【含注释】848行代码)!!!(包括模糊查询、分类查找、模拟拨号、qsort函数实现排序、文件存储、防误触等功能)_第23张图片

10、模拟拨号(它是像打印机一样慢慢一个数字一个数字打印的,这里演示不了)

实验课题——最全手机通信录实现版本(【含注释】848行代码)!!!(包括模糊查询、分类查找、模拟拨号、qsort函数实现排序、文件存储、防误触等功能)_第24张图片

11、模糊查询 实验课题——最全手机通信录实现版本(【含注释】848行代码)!!!(包括模糊查询、分类查找、模拟拨号、qsort函数实现排序、文件存储、防误触等功能)_第25张图片

12、把上次已经存好在文件中的信息加载过来:

当初次使用时:实验课题——最全手机通信录实现版本(【含注释】848行代码)!!!(包括模糊查询、分类查找、模拟拨号、qsort函数实现排序、文件存储、防误触等功能)_第26张图片

 如果已有信息(这里博主为了演示,把刚刚上面举的例子都删除了,所以现在的数据是博主又重新输入的,和上面的联系人信息存在不同,请见谅):

实验课题——最全手机通信录实现版本(【含注释】848行代码)!!!(包括模糊查询、分类查找、模拟拨号、qsort函数实现排序、文件存储、防误触等功能)_第27张图片

 现在已经保存好了:实验课题——最全手机通信录实现版本(【含注释】848行代码)!!!(包括模糊查询、分类查找、模拟拨号、qsort函数实现排序、文件存储、防误触等功能)_第28张图片

我们重启程序看看:实验课题——最全手机通信录实现版本(【含注释】848行代码)!!!(包括模糊查询、分类查找、模拟拨号、qsort函数实现排序、文件存储、防误触等功能)_第29张图片 成功读取了呢! 

好了!以上就是我的手机通信录的整个代码实现。

如果你认真看到这里,说明你应该对它的实现很感兴趣吧。

心动不如行动!实践是很重要的!现在,打开电脑,开始码代码吧。

博主建议,各位兄台先自己实现一下,如果遇到逻辑不通的地方,可以画一个图解帮助自己理解,

实在是遇到不理解的地方可以再回来看看。自己写一遍真的不一样!!!

部分复杂函数流程图

最后,因为两个结构体的关系有一点不好理解,所以博主画了一个总的说明图(图1):

实验课题——最全手机通信录实现版本(【含注释】848行代码)!!!(包括模糊查询、分类查找、模拟拨号、qsort函数实现排序、文件存储、防误触等功能)_第30张图片

                                                                      图1

下面是一些稍微复杂的函数的流程图(实验报告要求要画,我觉得比代码难搞1000倍!超累的!!!)

添加联系人函数:用于将新的联系人信息添加到通讯录中。该功能包括输入联系人的各项信息,然后将这些信息保存到通讯录中。在保存之前,可能需要进行一些验证和处理,例如检查通讯录是否已满、检查是否存在重复的姓名或电话号码等。

该函数用于将新的联系人信息添加到通讯录中。该功能包括输入联系人的各项信息,然后将这些信息保存到通讯录中。在保存之前,可能需要进行一些验证和处理,例如检查通讯录是否已满、检查是否存在重复的姓名、存入电话号码前先判断输入的是否为数字、是否存在重复的电话号码等,如图2-1-1所示。其中,添加联系人函数用到了5个小的功能函数,它们在“修改联系人函数”时也会再次被调用。find函数流程图用来寻找整个通信录中是否有该名字的字符串,如图2-1-2所示;findnum函数流程图用来寻找整个通信录中是否已有相同号码的字符串,如图2-1-3所示;isnumber函数流程图,用来判断输入号码中是否含有非数字字符,如图2-1-4所示;repetition_name函数流程图,用于判断是否有相同姓名的联系人,如图2-1-5所示;repetition_num函数,用于判断是否有相同号码的联系人,如图2-1-6所示。

实验课题——最全手机通信录实现版本(【含注释】848行代码)!!!(包括模糊查询、分类查找、模拟拨号、qsort函数实现排序、文件存储、防误触等功能)_第31张图片

图2-1-1 添加联系人功能(第1种实现)

实验课题——最全手机通信录实现版本(【含注释】848行代码)!!!(包括模糊查询、分类查找、模拟拨号、qsort函数实现排序、文件存储、防误触等功能)_第32张图片

                                                   图2-1-2 fun函数(查找同名联系人)

实验课题——最全手机通信录实现版本(【含注释】848行代码)!!!(包括模糊查询、分类查找、模拟拨号、qsort函数实现排序、文件存储、防误触等功能)_第33张图片

                图2-1-3 funnum函数(查找相同号码联系人)

实验课题——最全手机通信录实现版本(【含注释】848行代码)!!!(包括模糊查询、分类查找、模拟拨号、qsort函数实现排序、文件存储、防误触等功能)_第34张图片

                                             图2-1-4 isnumber函数(判断是否有非数字字符)

 实验课题——最全手机通信录实现版本(【含注释】848行代码)!!!(包括模糊查询、分类查找、模拟拨号、qsort函数实现排序、文件存储、防误触等功能)_第35张图片

                                                                 图2-1-5 repetition_name函数 

实验课题——最全手机通信录实现版本(【含注释】848行代码)!!!(包括模糊查询、分类查找、模拟拨号、qsort函数实现排序、文件存储、防误触等功能)_第36张图片

                                                                  图2-1-6 repetition_num函数

 修改联系人信息函数:在已有的通讯录中修改联系人的相关信息的函数。要求用户输入要修改的联系人姓名作为唯一标识信息,然后遍历整个通讯录,查找是否存在该联系人。如果存在该联系人,用户可以根据需要选择需要修改的信息并进行修改,修改后的信息会被更新到通讯录中,如果没有找到该联系人,则会给出相应的提示信息。

在已有的通信录中修改联系人的相关信息的函数,先判断通信录是否为空,若为空则提醒用户并退出,不为空则要求用户输入要修改的联系人姓名,作为唯一标识信息,然后遍历整个通信录,查找是否存在该联系人。若存在该联系人,用户可以根据需要选择需要修改的信息并进行修改,修改后的信息会被更新到通信录中,若没有找到该联系人,则会给出相应的提示信息。流程图如图3所示。

实验课题——最全手机通信录实现版本(【含注释】848行代码)!!!(包括模糊查询、分类查找、模拟拨号、qsort函数实现排序、文件存储、防误触等功能)_第37张图片 查找特定联系人函数:根据姓名或号码查找特定联络人信息。这个功能允许用户通过输入联系人的姓名或号码来查找该联系人的信息,如果找到,则返回该联系人的详细信息,否则返回查找失败的信息。

查找特定联系人信息流程图如图5所示。 

实验课题——最全手机通信录实现版本(【含注释】848行代码)!!!(包括模糊查询、分类查找、模拟拨号、qsort函数实现排序、文件存储、防误触等功能)_第38张图片

                                                        如图5  查找特定联系人信息

模糊查询函数:在通讯录中根据关键字模糊搜索联系人信息的功能。该功能可以让用户快速查找到包含特定关键字的联系人信息。根据输入的关键字,遍历通讯录中的所有联系人信息,查找包含关键字的联系人信息并输出。 

模糊查询函数如图6所示

实验课题——最全手机通信录实现版本(【含注释】848行代码)!!!(包括模糊查询、分类查找、模拟拨号、qsort函数实现排序、文件存储、防误触等功能)_第39张图片

                                                                    图6  模糊查询函数

本次内容到这里就结束啦!谢谢认真看到这里的你呀!

你可能感兴趣的:(进阶C语言,小游戏,通讯录的实现,智能手机,c语言,算法)