C语言----通讯录系统的实现-文件版本-创新互联

通讯录的最新版本来啦,请注意查收~~~

创新互联服务项目包括樊城网站建设、樊城网站制作、樊城网页制作以及樊城网络营销策划等。多年来,我们专注于互联网行业,利用自身积累的技术优势、行业经验、深度合作伙伴关系等,向广大中小型企业、政府机构等提供互联网行业的解决方案,樊城网站推广取得了明显的社会效益与经济效益。目前,我们服务的客户以成都为中心已经辐射到樊城省份的部分城市,未来相信会继续扩大服务区域并继续获得客户的支持与信任!

在手机中的通讯录,与我们之前的两个通讯录版本相比,大的区别是每次关掉程序或者关机重启,联系人的信息不会像在内存中的程序一样丢失,而是保存在了本地的硬盘里,这一次,我们就使用C语言中的文件功能对通讯录进行改进。

总结一下,需要完成的功能为:在退出时保存信息到文件;打开时加载文件中的信息到程序。

目录

一、代码优化

1.退出时保存信息

(1)创建函数

(2)保存信息

(3)关闭文件

2.打开程序时加载文件中的信息

(1)创建读取函数

(2)读取信息

(3)关闭文件

三、文件代码 

1.contact.h

2.contact.c

3.test.c

四、注意事项


一、代码优化         1.退出时保存信息

     我们创建SaveContact函数用来将信息保存至文件中,将这个函数放在switch语句中的case选项下,先存放到文件中,再销毁程序中的通讯录。

// test.c
...
case EXIT:
    SaveContact(&contact);
    DestoryContact(&contact);
    break;
...
(1)创建函数

     首先需要先打开文件,采用写入的方式,我们定义FILE类型指针,指向使用fopen打开的文件,并判断文件指针是否为空,即是否打开文件失败,失败就提示错误信息并退出函数。

(2)保存信息

      当打开文件成功后,就可以进行写入了,我们使用for循环进行逐个写入,使用fwrite写入文件,信息源为联系人列表,每次写入一个联系人信息所占的字节数,每次写入一个联系人信息,写入文件指针类型所指向的文件中。

(3)关闭文件

     最后就是关闭文件了,使用fclose关闭文件,并将指针置空。

     每次使用文件指针后切记要关闭文件,并将指针置空,防止内存访问异常!

     函数代码如下:

// contact.c
void SaveContact(Contact* pc)
{
	// 打开文件
	FILE* pf = fopen("contact.dat", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return;
	}
	// 写文件
	int i = 0;
	for (i = 0; i< pc->sz; i++)
	{
		// 要写入的信息来源为联系人列表;每次写入一个联系人的信息大小;
		// 每次写入一个联系人信息;写入pf所指向的文件中
		fwrite(pc->data+i, sizeof(PeoInfo), 1,pf);
	}

	// 关闭文件
	fclose(pf);
	pf = NULL;
}
2.打开程序时加载文件中的信息

加载文件就是指读取文件中的内容到程序中,我们创建LoadContact函数用来读取文件中的信息。

需要注意的是,我们应该在程序初始化时读取文件的内容,而不是到用户选择功能时再进行读取。将读取文件的函数放在初始化函数中。

// contact.c
void InitContact(Contact* pc)
{
    ...
    LoadContact(pc);
}
(1)创建读取函数

     a.读取文件

             使用FILE类型指针指向使用fopen打开存放信息的文件"contact.dat",需要注意的是,这里应当使用"r"对文件进行读取。要记得进行判断是否打开成功~

(2)读取信息

            使用fread对信息进行读取,创建一个临时的联系人列表temp用来存放读取得到的信息,并对temp进行判断:

      若读取为0表示文件中没有信息;

     若读取成功就将读取的信息存放到程序中创建的通讯录中,这时候文件中的联系人数量可能会大于我们设置的默认数量,需要判断是否需要对容量进行增加,我们使用CheckCapicaty函数进行判断。

// contact.c
void LoadContact(Contact* pc)
{
    ...
    CheckCapacity(pc);
    ...
}

     具体的CheckCapacity函数在动态版本中已有写明,可移步查看。

     我们将临时列表中的信息存放到程序中的通讯录中,使用while函数进行判断,当读取的信息不为空时,将联系人信息逐条转存到联系人列表中,为空时就退出程序。

(3)关闭文件

        在读取完成后,文件中的联系人信息已被存至程序中的通讯录中,直到退出通讯录时才需要再次打开文件进行写入,故在完成读取后需要关闭文件。

     函数代码如下:

// contact.c
void LoadContact(Contact* pc)
{
	FILE* pf = fopen("contact.dat", "r");
	if (pf == NULL)
	{
		perror("LoadContact");
		return;
	}
	// 读文件
	PeoInfo temp = { 0 };
	while (fread(&temp, sizeof(PeoInfo), 1, pf))
	{
		// 是否需要增容
		CheckCapacity(pc);
		pc->data[pc->sz] = temp;
		pc->sz++;
	}

	// 关闭文件
	fclose(pf);
	pf = NULL;
}
三、文件代码          1.contact.h
#define _CRT_SECURE_NO_WARNINGS 1

#pragma warning(disable:6031)

// 相关函数的声明
// 一些类型的定义

#include#include#include#define MAX_NAME 20
#define MAX_SEX 10
#define MAX_TEL 15
#define MAX_ADDR 30
#define MAX 1000
#define DEFAULT_SZ 3
#define INC_SZ 2


// 联系人的存放
// 使用typedef 定义结构体类型,方便调用
typedef struct PeoInfo
{
	char name[MAX_NAME];		// 姓名
	char sex[MAX_SEX];			// 性别
	int age;					// 年龄
	char tel[MAX_TEL];			// 电话
	char addr[MAX_ADDR];		// 地址
}PeoInfo;

// 通讯录
typedef struct Contact
{
	PeoInfo* data;	// 指向动态申请的空间,用来存放联系人的信息
	int sz;			// 通讯录中具体的联系人数量
	int capacity;	// 记录当前通讯录的大容量
}Contact;


// 初始化通讯录
void InitContact(Contact* pc);

// 增加联系人
void AddContact(Contact* pc);

// 打印联系人
void PrintContact(const Contact* pc);

// 删除联系人
void DelContact(Contact* pc);

// 查找联系人
void SearchContact(Contact* pc);

// 修改联系人
void ModifyContact(Contact* pc);

// 排序联系人
void SortContact(Contact* pc);

// 销毁通讯录
void DestorContact(Contact* pc);

// 保存通讯录信息
void SaveContact(Contact* pc);
2.contact.c
#define _CRT_SECURE_NO_WARNINGS 1

#pragma warning(disable:6031)


// 具体的功能函数的实现


#include"contact.h"

// 保存信息到文件中 
void SaveContact(Contact* pc)
{
	// 打开文件
	FILE* pf = fopen("contact.dat", "w");
	if (pf == NULL)
	{
		perror("SaveContact");
		return;
	}
	// 写文件
	int i = 0;
	for (i = 0; i< pc->sz; i++)
	{
		// 要写入的信息来源为联系人列表;每次写入一个联系人的信息大小;
		// 每次写入一个联系人信息;写入pf所指向的文件中
		fwrite(pc->data + i, sizeof(PeoInfo), 1, pf);
	}

	// 关闭文件
	fclose(pf);
	pf = NULL;
}


// 判断是否需要增容
void CheckCapacity(Contact* pc)
{
	if (pc->sz == pc->capacity)
	{
		PeoInfo* ptr = (PeoInfo*)realloc(pc->data, (pc->capacity + INC_SZ) * sizeof(PeoInfo));
		if (ptr != NULL)
		{
			pc->data = ptr;
			pc->capacity += INC_SZ;
			printf("增容成功\n");
		}
		else
		{
			perror("AddContact");
			printf("增加联系人失败\n");
			return;
		}
	}
}

void LoadContact(Contact* pc)
{
	FILE* pf = fopen("contact.dat", "r");
	if (pf == NULL)
	{
		// 提示错误并返回
		perror("LoadContact");
		return;
	}
	// 读取文件
	PeoInfo temp = { 0 };
	while (fread(&temp, sizeof(PeoInfo), 1, pf))
	{
		// 是否需要增容
		CheckCapacity(pc);
		pc->data[pc->sz] = temp;
		pc->sz++;
	}

	// 关闭文件
	fclose(pf);
	pf = NULL;
}


// 文件版本-初始化通讯录
void InitContact(Contact* pc)
{
	pc->data = (PeoInfo*)malloc(DEFAULT_SZ * sizeof(PeoInfo));
	if (pc->data == NULL)
	{
		perror("InitContact");
		return;
	}
	pc->sz = 0;
	pc->capacity = DEFAULT_SZ;

	// 加载文件
	LoadContact(pc);
}





// 动态版本——增加联系人
void AddContact(Contact* pc)
{
	// 判断空间是否足够
	CheckCapacity(pc);
	// 增加联系人信息
	printf("请输入姓名:>");
	scanf("%s", pc->data[pc->sz].name);
	printf("请输入性别:>");
	scanf("%s", pc->data[pc->sz].sex);
	printf("请输入年龄:>");
	// 变量需要使用&
	scanf("%d", &pc->data[pc->sz].age);
	printf("请输入电话:>");
	scanf("%s", pc->data[pc->sz].tel);
	printf("请输入地址:>");
	scanf("%s", pc->data[pc->sz].addr);

	// 使联系人数量+1
	pc->sz++;
	printf("增加成功\n");
}

// 动态版本——销毁通讯录
void DestorContact(Contact* pc)
{
	free(pc->data);
	pc->data = NULL;
	pc->sz = 0;
	pc->capacity = 0;
}

// 打印联系人——加上const确保在函数内部联系人信息不会被改变
void PrintContact(const Contact* pc)
{
	if (pc->sz == 0)
	{
		printf("通讯录中无联系人信息,无需打印\n");
		return;
	}
	// 打印表头
	printf("%-10s\t%-10s\t%-10s\t%-10s\t%-10s\n", "姓名", "性别", "年龄", "电话", "地址");

	// 打印联系人信息
	int i = 0;
	for (i = 0; i< pc->sz; i++)
	{
		printf("%-10s\t%-10s\t%-10d\t%-10s\t%-10s\n",
			pc->data[i].name,
			pc->data[i].sex,
			pc->data[i].age,
			pc->data[i].tel,
			pc->data[i].addr
		);
	}
	printf("打印完成\n");
}

// 通过姓名查找联系人
// 创建查找函数,方便被删除修改和查找时调用
// 使用static 使得此函数只能在本函数内部被调用和查看
static int FindName(const Contact* pc, const char input_name[])
{
	int i = 0;
	for (i = 0; i< pc->sz; i++)
	{
		// strcmp的结果为0时是找到了对应的姓名
		if (strcmp(pc->data[i].name, input_name) == 0)
		{
			// 则返回对应的联系人信息下标
			return i;
		}
	}
	// 找不到
	return -1;
}



// 删除联系人
void DelContact(Contact* pc)
{
	// 判断通讯录是否为空
	if (pc->sz == 0)
	{
		printf("通讯录为空,无需删除\n");
		return;
	}
	// 查找要删除的人
	char input_name[MAX_NAME];
	printf("请输入要删除人的姓名:>");
	scanf("%s", input_name);
	// 设定ret判断返回结果
	int ret = FindName(pc, input_name);
	// 当查找失败时
	if (ret == -1)
	{
		printf("要删除的人不存在\n");
		return;
	}
	// 删除后,将删除联系人之后的联系人信息的下标-1
	int i = 0;
	for (i = ret; i< pc->sz; i++)
	{
		pc->data[i] = pc->data[i + 1];
	}
	// 删除成功后使联系人数量减1
	pc->sz--;
	printf("删除成功\n");
}

// 查找联系人
void SearchContact(Contact* pc)
{
	char find_name[MAX_NAME] = { 0 };
	printf("请输入查找的联系人姓名:>");
	scanf("%s", find_name);
	int ret = FindName(pc, find_name);
	if (ret == -1)
	{
		printf("查无此人\n");
		return;
	}
	else
	{
		printf("%-10s\t%-10s\t%-10s\t%-10s\t%-10s\n", "姓名", "性别", "年龄", "电话", "地址");
		printf("%-10s\t%-10s\t%-10d\t%-10s\t%-10s\n",
			pc->data[ret].name,
			pc->data[ret].sex,
			pc->data[ret].age,
			pc->data[ret].tel,
			pc->data[ret].addr
		);
		printf("查找完毕\n");
	}
}

// 修改联系人
void ModifyContact(Contact* pc)
{
	char input_name[MAX_NAME] = { 0 };
	printf("请输入要修改的联系人的姓名:>");
	scanf("%s", input_name);
	int ret = FindName(pc, input_name);
	if (ret == -1)
	{
		printf("查无此人,无法修改\n");
		return;
	}
	printf("请输入姓名:>");
	scanf("%s", pc->data[ret].name);
	printf("请输入性别:>");
	scanf("%s", pc->data[ret].sex);
	printf("请输入年龄:>");
	// 变量需要使用&
	scanf("%d", &pc->data[ret].age);
	printf("请输入电话:>");
	scanf("%s", pc->data[ret].tel);
	printf("请输入地址:>");
	scanf("%s", pc->data[ret].addr);
	printf("修改完毕\n");
}

// 创建比较函数
int cmp_name(const void* p1, const void* p2)
{
	return strcmp(((PeoInfo*)p1)->name, ((PeoInfo*)p2)->name);
}

// 排序联系人
void SortContact(Contact* pc)
{
	// 判断通讯录是否为空
	if (pc->sz == 0)
	{
		printf("通讯录为空,无需排序\n");
		return;
	}
	// 以姓名为排序依据进行排序

	// 使用qsort函数进行排序——调用了创建的cmp_name()
	// 待排序列地址、待排关键字个数、关键字大小、比较函数地址
	qsort(pc->data, pc->sz, sizeof(PeoInfo), cmp_name);

	printf("排序成功\n");

}
3.test.c
#define _CRT_SECURE_NO_WARNINGS 1

#pragma warning(disable:6031)

// 测试文件

// 文件版本
// 当退出通讯录时,将联系人信息保存到文件中
// 当打开通讯录时,加载文件中的联系人信息到程序中
// 

#include"contact.h"


void menu()
{
	printf("*******************\n");
	printf("****  1.add    ****\n");		// 增加联系人信息
	printf("****  2.del    ****\n");		// 删除联系人信息
	printf("****  3.serach ****\n");		// 查找联系人信息
	printf("****  4.modify ****\n");		// 修改联系人信息
	printf("****  5.sort   ****\n");		// 排序联系人信息
	printf("****  6.print  ****\n");		// 打印联系人信息
	printf("****  0.exit   ****\n");		// 退出通讯录
	printf("*******************\n");
}

// 使用枚举,使得switch选择时,更加明了
enum Menu
{
	EXIT,		// 默认为 0 
	ADD,		// 1
	DEL,
	SEARCH,
	MODIFY,
	SORT,
	PRINT
};

int main()		// 在静态模式下会因为数据过多报警告
{
	int input = 0;

	// 创建通讯录
	Contact contact;

	// 初始化通讯录
	InitContact(&contact);

	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case ADD:
			AddContact(&contact);
			break;
		case DEL:
			DelContact(&contact);
			break;
		case SEARCH:
			SearchContact(&contact);
			break;
		case MODIFY:
			ModifyContact(&contact);
			break;
		case SORT:
			SortContact(&contact);
			break;
		case PRINT:
			PrintContact(&contact);
			break;
		case EXIT:
			// 保存信息到文件
			SaveContact(&contact);
			DestorContact(&contact);
			printf("程序结束\n");
			break;
		default:
			printf("选择错误,请重新选择:\n");
		}
	} while (input);
	return 0;
}
四、注意事项

1.在之前两个版本中,有少许代码无用,不影响功能的执行,在当前版本进行了适当优化。

2.目前为止的三个版本分别对应不同的C语言内容,可根据自身学习进度进行实现,做到先理解,再独立完成。不要没有学习对应的内容就进行编写,一口吃不成胖子,学习急不得。

3.通讯录的编写告一段落,日后若有新的想法或者更新,将会编写相关的文章用来讲述。

4.gitee链接

shiwuqing-Contact-3

     

你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧


本文标题:C语言----通讯录系统的实现-文件版本-创新互联
分享路径:http://ybzwz.com/article/dcecdd.html