C语言实现简易通讯录3.0(通讯录信息可以向文件写入,从文件读取)

我们在前文中介绍了如何实现简易通讯录
后来又改进了通讯录,使之可以进行动态内存管理。
但是,我们的通讯录程序是只能一次性使用的,退出程序所有的内容都消失了,那么本文就来实现将通讯录的数据写入到硬盘上的文件。再次打开程序时,再从文件上读取数据。实现数据的持久性。

代码如下:

addressBook.h文件

#pragma once

//头文件

#include 
#include 
#include 

#define MAX_NAME 20
#define MAX_SEX 6
#define MAX_TELE 12
#define MAX_ADDR 20
#define MAX 1000

#define INIT_SZ 3
#define CAP_ADD 2

enum fun
{
	EXIT,//从0开始,EXIT = 0
	ADD,//1
	DEL,//2
	SEARCH,//3
	MODIFY,//4
	PRINT,
	SORT,
	DESTORY
};



//个人信息结构体:
typedef struct PeoInfo
{
	char name[MAX_NAME];
	int age;
	char sex[MAX_SEX];
	char tele[MAX_TELE];
	char addr[MAX_ADDR];
}PeoInfo;

通讯录结构体:静态版本
//typedef struct Book
//{
//	PeoInfo data[MAX];//
//	int size;//记录当前通讯录中有效的信息个数
//}Book;

//通讯录结构体:动态版本
typedef struct Book
{
	PeoInfo* data;//动态版本:记录个人信息
	int size;//记录当前通讯录中有效的信息个数
	int cap;//记录当前的最大容量,以便满了通过动态内存管理来扩充
}Book;

//菜单函数声明:
void menu(void);
void menu_ins(void);

//初始化通讯录函数声明
void InitMsg(Book*);

//增加通讯录信息函数声明:
void AddMsg(Book* );

//打印通讯录信息函数声明:
void PrintMsg(Book* );

//删除通讯录信息函数声明:
void DelMsg(Book* );

//查找通讯录信息函数声明:
void SearchMsg(Book*);

//修改通讯录信息函数声明:
void ModMsg(Book*);

//排序通讯录信息函数声明:
void SortMsg(Book*);

//通讯录动态空间释放函数声明:
void DestroyMsg(Book*);

//检查是否需要增容:
void CheckCap(Book* pc);

//保存通讯录信息到文件中的函数声明:
void SaveMsg(Book*);

//程序打开,初始化通讯录时,加载文件中的数据到通讯录中函数声明:
void LoadMsg(Book*);

//删除文件中通讯录信息,清空通讯录文件
void nothing(Book* pc);

addressBook.c文件

//
//本文件包括  各功能函数:


#include "addressBook.h"

//菜单显示:
void menu()
{
	printf("********************************\n");
	printf("******  1.增加    2.删去  ******\n");
	printf("******  3.搜索    4.修改   *****\n");
	printf("******  5.浏览    6.排序   *****\n");
	printf("******  0.退出    7.销毁  *******\n");
	printf("********************************\n");
}
//菜单说明:
void menu_ins()
{
	printf("菜单说明\n");
	printf("增加:增加通讯录信息\n");
	printf("删去:输入姓名,删去该姓名对应的通讯录信息\n");
	printf("搜索:输入姓名,搜索并打印该姓名对应的通讯录信息\n");
	printf("修改:输入姓名,修改该姓名对应的通讯录信息\n");
	printf("浏览:打印出全部通讯录信息\n");
	printf("排序:根据姓名/年龄排序通讯录信息\n");
	printf("销毁:清空通讯录及文件中的数据信息\n");
}

//通过姓名来寻找某条通讯录信息函数:
static int FindByName(Book* pc, char* name)
{
	for (int i = 0; i < pc->size; i++)
	{
		if (strcmp(name, pc->data[i].name) == 0)//strcmp来判断是不是相同
		{
			return i;
		}
	}
	return -1;//没有找到,返回-1
}

//初始化时加载文件中的数据到通讯录中:
void LoadMsg(Book* pc)
{
	//打开文件
	FILE* pf = fopen("addressBook.txt", "r");
	//检查文件打开成功与否
	if (pf == NULL)
	{
		perror("LoadMsg");
		printf("加载文件出错!\n");
	}
	
	//开始加载数据:
	PeoInfo tmp = { 0 };
	//巧妙使用fread函数返回值来一直读取数据
	while (fread(&tmp, sizeof(PeoInfo), 1, pf))
	{
		//是否需要增容
		CheckCap(pc);
		pc->data[pc->size] = tmp;
		pc->size++;
	}

	//关闭文件
	fclose(pf);
	pf = NULL;
	printf("加载文件成功!\n");
}

//通讯录初始化函数:
//和静态版本不同,这里data没有初始化大小,因此需要在这个初始化函数中给它分配一个动态空间
void InitMsg(Book* pc)
{
	pc->data = (PeoInfo*)malloc(INIT_SZ * sizeof(PeoInfo));
	if (pc->data == NULL)
	{
		perror("初始化函数InitMsg出现错误\n");
		return;
	}
	pc->size = 0;
	pc->cap = INIT_SZ;

	//打开程序,初始化时从文件中加载数据:
	LoadMsg(pc);
}

//销毁通讯录,清空文件信息
void nothing(Book* pc)
{
	//打开文件以写的形式
	FILE* pf = fopen("addressBook.txt", "w");

	//检查打开成功与否
	if (pf == NULL)
	{
		perror("SaveMsg");
		printf("保存失败!\n");
		return;
	}

	//清空通讯录信息
	//每次保存一条空信息进去,循环保存pc->size次将所有信息清楚
	PeoInfo tmp = { 0 };
	for (int i = 0; i < pc->size; i++)
	{
		fwrite(&tmp, sizeof(PeoInfo), 1, pf);
	}
	pc->size = 0;//通信录信息计数清零
	pc->cap = INIT_SZ;//通讯录大小恢复初始值

	fclose(pf);
	pf = NULL;
	printf("文件已销毁\n");
}



增加通讯录信息功能函数:静态版本:
//void AddMsg(Book* pc)
//{
//	printf("请输入姓名:");
//	scanf("%s", pc->data[pc->size].name);
//	printf("请输入年龄:");
//	scanf("%d", &(pc->data[pc->size].age));
//	printf("请输入性别:");
//	scanf("%s", pc->data[pc->size].sex);
//	printf("请输入电话:");
//	scanf("%s", pc->data[pc->size].tele);
//	printf("请输入地址:");
//	scanf("%s", pc->data[pc->size].addr);
//
//	pc->size++;//增加一个后,通讯录内的下表要自加1,以填入下一个信息。否则以前的信息将被覆盖。
//
//	printf("增加成功\n");
//
//}


//检查是否需要增容:
void CheckCap(Book* pc)
{
	if (pc->size == pc->cap)//容量达到当前最大容量,需要扩容
	{
		PeoInfo* ptr = realloc(pc->data, (CAP_ADD + pc->cap) * sizeof(PeoInfo));//realloc函数第二个参数是原空间+需要调整的大小

		if (ptr != NULL)
		{
			pc->data = ptr;
			pc->cap += CAP_ADD;
			printf("扩容成功!\n");
		}
		else
		{
			perror("通讯录扩容出现错误\n");
			printf("扩容失败!\n");
		}

	}
}


//增加通讯录信息功能函数:动态版本:
void AddMsg(Book* pc)
{
	CheckCap(pc);

	printf("请输入姓名:");
	int rescanf = scanf("%s", pc->data[pc->size].name);
	printf("请输入年龄:");
	rescanf = scanf("%d", &(pc->data[pc->size].age));
	printf("请输入性别:");
	rescanf = scanf("%s", pc->data[pc->size].sex);
	printf("请输入电话:");
	rescanf = scanf("%s", pc->data[pc->size].tele);
	printf("请输入地址:");
	rescanf = scanf("%s", pc->data[pc->size].addr);

	pc->size++;//增加一个后,通讯录内的下表要自加1,以填入下一个信息。否则以前的信息将被覆盖。

	printf("增加成功\n");

}



//打印浏览通讯录信息功能函数:
void PrintMsg(Book* pc)
{
	if (pc->size == 0)//判断通讯录是不是空的
	{
		printf("通讯录为空!\n");
		return;
	}
	//打印标题栏:
	printf("%-10s\t%-4s\t%-6s\t%-12s\t%-20s\n", "姓名", "年龄", "性别", "电话", "地址");

	//打印内容:
	for (int i = 0; i < pc->size; i++)
	{
		printf("% -10s\t% -4d\t% -6s\t% -12s\t% -20s\n", pc->data[i].name, 
													pc->data[i].age, 
													pc->data[i].sex, 
													pc->data[i].tele, 
													pc->data[i].addr);
	}
	printf("打印成功!\n");
}




//删除通讯录信息功能函数:
void DelMsg(Book* pc)
{
	if (pc->size == 0)//判断通讯录是不是空的
	{
		printf("通讯录为空,无需删除\n");
		return;
	}
	char name[MAX_NAME] = { 0 };
	printf("请输入要删除的人的姓名:\n");
	int rescanf = scanf("%s", name);
	//1.查找这个名字:
	int ret = FindByName(pc, name);
	//找不到:
	if (ret == -1)
	{
		printf("查无此人\n");
		return;
	}
	//找到了
	//2.开始删除:
	else
	{
		for (int i = ret; i < pc->size - 1; i++)
		{
			pc->data[i] = pc->data[i + 1];//删除其实就是让后一个向前覆盖
		}
	}
	pc->size--;//因为删除了一个,所以pc->size也相应-1
	printf("删除成功!\n");
}


//查找并打印通讯录某一条信息功能函数:
void SearchMsg(Book* pc)
{
	if (pc->size == 0)//判断通讯录是不是空的
	{
		printf("通讯录为空!\n");
		return;
	}
	char name[MAX_NAME] = { 0 };
	printf("请输入要查找的人的姓名:\n");
	int rescanf = scanf("%s", name);
	//1.查找这个名字:
	int i = FindByName(pc, name);
	//找不到:
	if (i == -1)
	{
		printf("查无此人\n");
		return;
	}

	//找到了
	//2.开始打印:
	
	else
	{
		printf("查找成功,信息如下>\n");
		printf("%-10s\t%-4s\t%-6s\t%-12s\t%-20s\n", "姓名", "年龄", "性别", "电话", "地址");
		printf("% -10s\t% -4d\t% -6s\t% -12s\t% -20s\n", pc->data[i].name,
			pc->data[i].age,
			pc->data[i].sex,
			pc->data[i].tele,
			pc->data[i].addr);
	}
	printf("查找成功!\n");
}


//修改某人信息功能函数:
void ModMsg(Book* pc)
{
	if (pc->size == 0)//判断通讯录是不是空的
	{
		printf("通讯录为空!\n");
		return;
	}
	char name[MAX_NAME] = { 0 };
	printf("请输入要修改的人的姓名:\n");
	int rescanf = scanf("%s", name);
	//1.查找这个名字:
	int i = FindByName(pc, name);
	//找不到:
	if (i == -1)
	{
		printf("查无此人\n");
		return;
	}
	else
	{
		printf("请输入修改后姓名:");
		rescanf = scanf("%s", pc->data[i].name);
		printf("请输入修改后年龄:");
		rescanf = scanf("%d", &(pc->data[i].age));
		printf("请输入修改后性别:");
		rescanf = scanf("%s", pc->data[i].sex);
		printf("请输入修改后电话:");
		rescanf = scanf("%s", pc->data[i].tele);
		printf("请输入修改后地址:");
		rescanf = scanf("%s", pc->data[i].addr);

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


//排序功能实现:
//根据姓名或者年龄排:
//
//1.根据姓名排:
static int sort_name(const void* e1, const void* e2)
{
	return strcmp(((PeoInfo*)e1)->name, ((PeoInfo*)e2)->name);
}
static void SortByName(Book* pc)
{
	qsort(pc->data,pc->size, sizeof(pc->data[0]), sort_name);
	//此处第二个参数是要传需排序的元素的个数,也就是有多少条通讯录信息
	//因为我们专卖在结构体Book里加入了计数变量size,所以此处传size即可
}

//2.根据年龄排:
static int sort_age(const void* e1, const void* e2)
{
	return ((PeoInfo*)e1)->age - ((PeoInfo*)e2)->age;
}
static void SortByAge(Book* pc)
{
	qsort(pc->data,pc->size , sizeof(pc->data[0]), sort_age);
}

//排序函数:
void SortMsg(Book* pc)
{
	if (pc->size == 0)//判断通讯录是不是空的
	{
		printf("通讯录为空!\n");
		return;
	}
	
	int input = 0;
	

	printf("******  请选择排序方式   **********\n");
	printf("********   1.姓名    ************\n");
	printf("********   2.年龄    ************\n");
	printf("********************************\n");

	int rescanf = scanf("%d", &input);
	switch (input)
	{
	case 1:
		SortByName(pc);
		break;
	case 2:
		SortByAge(pc);
		break;
	}

}


//通讯录动态空间释放函数声明:
void DestroyMsg(Book* pc)
{
	free(pc->data);
	pc->data = NULL;
	pc->size = 0;
	pc->cap = 0;
	printf("退出通讯录\n");
}

//保存通讯录信息到文件中:
void SaveMsg(Book* pc)
{
	//打开文件以写的形式
	FILE* pf = fopen("addressBook.txt", "w");

	//检查打开成功与否
	if (pf == NULL)
	{
		perror("SaveMsg");
		printf("保存失败!\n");
		return;
	}

	//保存通讯录信息
	//每次保存一条,循环保存pc->size次保存完整
	for (int i = 0; i < pc->size; i++)
	{
		fwrite(&pc->data[i], sizeof(PeoInfo), 1, pf);
	}

	fclose(pf);
	pf = NULL;
	printf("文件已保存\n");
}

main.c


//要求:
//
//1. 该通讯录能存放1000个人的信息。每个人的信息包括:姓名 + 年龄 + 性别 + 电话 + 地址;
//2. 增加通讯录信息;
//3. 删去通讯录信息;
//4. 查找通讯录信息;
//5. 修改通讯录信息;
//6. 打印浏览通讯录信息。



#include "addressBook.h"

int main()
{
	int input = 0;

	Book myBook ;
	InitMsg(&myBook);//定义一个通讯录并初始化

	menu_ins();

	do {

		menu();//打印菜单,以供选择
		printf("请选择功能 >\n");

		int rescanf = scanf("%d", &input);

		switch (input)
		{
			//1.增加通讯录信息:
		case ADD:	
			AddMsg(&myBook);
			break;

			//2.删除通讯录信息:
		case DEL:
			DelMsg(&myBook);
			break;

			//3.查找通讯录信息:
		case SEARCH:
			SearchMsg(&myBook);
			break;

			//4.修改通讯录信息:
		case MODIFY:
			ModMsg(&myBook);
			break;

			//5.打印浏览通讯录信息:
		case PRINT:
			PrintMsg(&myBook);
			break;

			//6.根据姓名/年龄/性别排序:
		case SORT:
			SortMsg(&myBook);
			break;

			//退出程序:
		case EXIT:
			SaveMsg(&myBook);//保存通讯录信息到文件
			DestroyMsg(&myBook);//释放通讯录动态内存空间
			break;

			//销毁通讯录文件信息
		case DESTORY:
			nothing(&myBook);
			break;

		default:
			printf("输入错误,重新输入>\n");
			break;
		}

	} while (input);
}

你可能感兴趣的:(C/C++实例,c语言)