可文件存储的动态通讯录(C语言)

目录

通讯录结构体

第一次启动通讯录

通讯录初始化

通讯录扩容

导出文件中存储的数据

数据导入文件

传送门(全代码)

通讯录是个结构体数组,而数组的特性就是一次给定一个固定的大小之后是不可以增容的,但是如果学习了动态开辟,使用malloc和realloc在堆区开辟的数组就可以解决掉数组固定大小的问题。

另一个问题就是在程序结束后,上一次存储的数据也会随着程序的结束消失,这也就是为什么你玩某些单机游戏为什么要存档的原因,但是如果你打开游戏目录是可以找到你的存档放置的文件夹的。那可文件存储的通讯录,可能就是游戏使用了某些特殊的存储方式,而我们使用的是记事本来存储。

通讯录结构体

通讯录首先肯定要先定义一个通讯录数组的结构体和一个联系人信息结构体。联系人结构体就随便你咋定义它的属性,只要像对它的数据类型应该是什么就行,最好可以用常量来定义内容的大小,方便日后维护。

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

//重命名结构体方便使用
typedef struct PosenInfo
{
	char name[MAX];
	int age;
	char sex[SEX_MAX];
	char addr[ADDR_MAX];
	char tele[TELE_MAX];
}PosenInfo;

重点在通讯录的数组结构体,因为是动态开辟,所以肯定需要用一个记录容量的结构体成员,当前结构体使用了个空间,还有一个联系人结构体数组。

typedef struct Contact
{
	PosenInfo* data;//数据
	int size;//记录数组当前使用了多少空间
	int capacity;//记录数组容量
}Contact;

第一次启动通讯录

搞定通讯录结构体后,因为该通讯录的可以存储文件的。首先是想如何把文件给导入到程序里,要把程序导入到文件,要使用读文件的方式。但是第一次没有文件的时候,如果使用读文件的方式打开文件,那肯定直接报错。

所以不管三七二十一,第一步肯定是先自动创建一个文件,也就是说要用写的方式打开,然后什么也不干直接关闭文件,这样就自动创建文件了。

	//创建一个二进制文件
	FILE* pf = fopen("contact.txt", "wb");;
	fclose(pf);

通讯录初始化

初始化要考虑的是开辟动态数组并将数组内容赋初值,还要考虑容量满了之后的增容问题,以及将文件信息导入程序。

首先开数组,由于初始化内容需要将文件导入到通讯录中,所以就可以交给另一个函数解决。只需要把数组开好进行,并把初始容量给定好就行。由于联系人结构体刚开始什么都不放,所以我直接用calloc偷个懒。

void InitContact(Contact* pcon)
{
	assert(pcon);
	pcon->size = 0;
	PosenInfo* tmp = (PosenInfo*)calloc(DEFAULT_SIZE, sizeof(PosenInfo));
	if (tmp != NULL)
	{
		pcon->data = tmp;
	}
	else
	{
		printf("Init_Contact()::%s\n", strerror(errno));
		//空间开辟失败可以直接结束程序了
		exit(-1);
	}
    //DEFAULT_SIZE 随便给多少
	pcon->capacity = DEFAULT_SIZE;
    
    //导出文件数据
	LoadContact(pcon);
}

通讯录扩容

然后需要将上一次文件存入文件后,再导出到这次程序的问题。打开文件又涉及到一个问题,如果你的容量存不下导出的文件怎么办?是不是应该扩容一下。所以先不着急写导出而是应该先写一个扩容函数。

扩容的问题就交给一个独立的函数解决。首先是要检查容量,当容量满了才会扩容。

扩容用到了realloc。那么扩多大比较科学呢?如果一次只扩几个空间可能会导致realloc频繁扩容,realloc有两种扩容方式,一种是原地扩容,另一种是异地扩容,如果是原地扩容还好说,但是异地扩容就对时间的开销比较大,通讯录以时间换空间明显是不太划算的。所以为了避免频繁的扩容,建议扩2倍是比较中和的,也不要拘泥那没有用完的空间,因为只要还是数组,那就不能避免多多少少的浪费,这点在数据结构的顺序表就能理解。

static void CheckCapacity(Contact* pcon)
{
	assert(pcon);
    //没满就返回
	if (pcon->size != pcon->capacity)
		return;
    //满了
	Contact* tmp = (Contact*)realloc(pcon->data, sizeof(Contact) * (pcon->capacity * 2));
	if (tmp != NULL)
	{
		//增容
		pcon = tmp;
        //这个一定要改对
		pcon->capacity *= 2;
	}
	else
	{
		//增容失败
		printf("check_capacity()::%s", strerror(errno));
		exit(-1);
	}
}

导出文件中存储的数据

导出数据肯定要以读的方式解决,因为创建文件和增容的问题都已经被解决了,所以可以直接写肆无忌惮的写代码了。只需要用一个中间的数据接收文件读取的数据,再将数据赋值给结构体的data的size为位置,最后数组成员size++一下就搞定了。

//加载文件数据
static void LoadContact(Contact* pcon)
{
	assert(pcon);
	//读方式打开二进制文件
	FILE* pf = fopen("contact.txt", "rb");
	if (pf == NULL)
	{
		printf("LoadContact()::%s\n", strerror(errno));
		return;
	}
	PosenInfo buf = { 0 };
	while (fread(&buf, sizeof(PosenInfo), 1, pf))
	{
		//检查容量
		CheckCapacity(pcon);

		pcon->data[pcon->size] = buf;
		pcon->size++;
	}
	//关闭文件
	fclose(pf);
    //置不置空都可以,因为没人可以访问到pf了
	//pf = NULL;
}

数据导入文件

数据导入文件也非常简单,先以写的方式打开文件,然后导入结构体的数据就行了。

//保存数据到文件
void ContactSave(Contact* pcon)
{
	//打开写的方式打开二进制文件
	FILE* pf = fopen("contact.txt", "wb");
	if (pf == NULL)
	{
		//打开失败了
		printf("SaveContact()::%s\n", strerror(errno));
		return;
	}
	for (int i = 0;i < pcon->size;i++)
	{
		//导数据
		fwrite(pcon->data + i, sizeof(PosenInfo), 1, pf);
	}
	fclose(pf);
	//pf = NULL;
}

传送门(全代码)

增删查改就不写了,有基础都可以写出来(都看通讯录了肯定有基础的吧)。思想不同写出来的也不同,我写的东西可能就不兼容你写的东西了。比如有个地方查的电话,另一个查的又是名字。我是直接排序,有些可能会封装一个函数来排序,像这种问题所以也就懒得写了,但是注意的是堆区开辟的代码一定要被释放。

//contact.h
#pragma once

#include
#include
#include
#include
#include

//最初默认容量
#define DEFAULT_SIZE 3

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

//枚举方便观察调用哪个功能
enum Optione
{
	EXIT,
	ADD,
	DELETE,
	FIND,
	MODIFY,
	SHOW
};

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

typedef struct Contact
{
	PosenInfo* data;
	int size;
	int capacity;
}Contact;

//初始化
void InitContact(Contact* pcon);
//添加联系人
void ContactAdd(Contact* pcon);
//删除
void ContactDelete(Contact* pcon);
//查询
void ContactFind(const Contact* pcon);
//修改
void ContactMonify(Contact* pcon);
//打印通讯录
void ContactShow(const Contact* pcon);
//释放空间
void ContactDestroy(Contact* pcon);
//保存文件
void ContactSave(Contact* pcon);
//contact.c

#include"contact.h"


static int ContactTmp(const void* e1, const void* e2)
{
	return strcmp(((PosenInfo*)e1)->name, ((PosenInfo*)e2)->name);
}

static void CheckCapacity(Contact* pcon)
{
	assert(pcon);

	if (pcon->size != pcon->capacity)
		return;
	Contact* tmp = (Contact*)realloc(pcon->data, sizeof(Contact) * (pcon->capacity * 2));
	if (tmp != NULL)
	{
		//增容
		pcon = tmp;
		pcon->capacity += 2;
	}
	else
	{
		//增容失败
		printf("check_capacity()::%s", strerror(errno));
		exit(-1);
	}
}

//加载文件数据
static void ContactLoad(Contact* pcon)
{
	assert(pcon);
	//打开文件
	FILE* pf = fopen("contact.txt", "rb");
	if (pf == NULL)
	{
		printf("LoadContact()::%s\n", strerror(errno));
		return;
	}
	PosenInfo buf = { 0 };
	while (fread(&buf, sizeof(PosenInfo), 1, pf))
	{
		//检查容量
		CheckCapacity(pcon);

		pcon->data[pcon->size] = buf;
		pcon->size++;
	}
	//关闭文件
	fclose(pf);
	pf = NULL;
}

void InitContact(Contact* pcon)
{
	assert(pcon);
	pcon->size = 0;
	PosenInfo* tmp = (PosenInfo*)calloc(DEFAULT_SIZE, sizeof(PosenInfo));
	if (tmp != NULL)
	{
		pcon->data = tmp;
	}
	else
	{
		printf("Init_Contact()::%s\n", strerror(errno));
		//空间开辟失败可以直接结束程序了
		exit(-1);
	}
	pcon->capacity = DEFAULT_SIZE;

	LoadContact(pcon);
}

void ContactAdd(Contact* pcon)
{
	assert(pcon);
	//检查容量
	check_capacity(pcon);

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

	pcon->size++;
}


static int FindByTELE(const Contact* pcon)
{
	char tele[TELE_MAX] = { 0 };
	printf("请输入联系人电话:>");
	scanf("%s", tele);
	int i = 0;
	for (i = 0;i < pcon->size;i++)
	{
		if (strcmp(pcon->data[i].tele, tele) == 0)
			return i;
	}
	return -1;
}

void ContactDelete(Contact* pcon)
{
	assert(pcon);
	if (pcon->size == 0)
	{
		printf("通讯录为空\n");
		return;
	}
	int i = 0;
	int pos = FindByTELE(pcon);
	if (pos == -1)
	{
		printf("联系人不存在\n");
		return;
	}
	for (i = pos;i < pcon->size;i++)
	{
		pcon->data[i] = pcon->data[i + 1];
	}
	printf("删除成功\n");
	pcon->size--;
}

void ContactShow(const Contact* pcon)
{
	assert(pcon);

	//排序
	if (pcon->size > 1)
		qsort(pcon->data, pcon->size, sizeof(pcon->data[0]), ContactTmp);
	int i = 0;
	printf("%-10s\t%-5s\t%-5s\t%-30s\t%-13s\n",
		"姓名", "年龄", "性别", "地址", "电话");
	for (i = 0; i < pcon->size;i++)
	{
		printf("%-10s\t%-5d\t%-5s\t%-30s\t%-13s\n",
			pcon->data[i].name,
			pcon->data[i].age,
			pcon->data[i].sex,
			pcon->data[i].addr,
			pcon->data[i].tele);
	}
}

void ContactFind(const Contact* pcon)
{
	assert(pcon);
	if (pcon->size == 0)
	{
		printf("通讯录为空\n");
		return;
	}
	char name[TELE_MAX] = { 0 };
	printf("请输入联系人姓名:>");
	scanf("%s", name);
	int flag = 1;
	for (int i = 0;i < pcon->size;i++)
	{
		//查询所有该姓名的联系人
		if (strcmp(pcon->data[i].name, name) == 0)
		{
			printf("%-10s\t%-5s\t%-5s\t%-30s\t%-13s\n",
				"姓名", "年龄", "性别", "地址", "电话");
			printf("%-10s\t%-5d\t%-5s\t%-30s\t%-13s\n",
				pcon->data[i].name,
				pcon->data[i].age,
				pcon->data[i].sex,
				pcon->data[i].addr,
				pcon->data[i].tele);
			flag = 0;
		}
	}
	if (flag)
	{
		printf("联系人不存在\n");
	}
}

void ContactMonify(Contact* pcon)
{
	assert(pcon);
	if (pcon->size == 0)
	{
		printf("通讯录为空\n");
		return;
	}
	int i = 0;
	int pos = FindByTELE(pcon);
	if (pos == -1)
	{
		printf("联系人不存在\n");
		return;
	}
	printf("请输入姓名:>");
	scanf("%s", pcon->data[pos].name);
	printf("请输入年龄:>");
	scanf("%d", &pcon->data[pos].age);
	printf("请输入性别:>");
	scanf("%s", pcon->data[pos].sex);
	printf("请输入地址:>");
	scanf("%s", pcon->data[pos].addr);
	printf("请输入电话:>");
	scanf("%s", pcon->data[pos].tele);

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

//释放空间
void ContactDestroy(Contact* pcon)
{
	free(pcon->data);
	pcon->data = NULL;
	pcon->capacity = 0;
	pcon->size = 0;
}


//保存数据到文件
void ContactSave(Contact* pcon)
{
	//打开写的方式打开二进制文件
	FILE* pf = fopen("contact.txt", "wb");
	if (pf == NULL)
	{
		//打开失败了
		printf("SaveContact()::%s\n", strerror(errno));
		return;
	}
	for (int i = 0;i < pcon->size;i++)
	{
		//导数据
		fwrite(pcon->data + i, sizeof(PosenInfo), 1, pf);
	}
	fclose(pf);
	//pf = NULL;
}
//main.c
#include"contact.h"

void menu()
{
	printf("****************************************\n");
	printf("*********  1.add      2.del    *********\n");
	printf("*********  3.find   4.modifa *********\n");
	printf("********   5.show     0.exit   *********\n");
	printf("****************************************\n");
}


void contact()
{
	int input = 0;
	Contact con;
	Init_Contact(&con);
	//创建一个二进制文件
	FILE* pf = fopen("contact.txt", "wb");;
	fclose(pf);
	do
	{
		menu();
		scanf("%d", &input);
		switch (input)
		{
		case ADD:
			ContactAdd(&con);
			break;
		case DELETE:
			ContactDelete(&con);
			break;
		case FIND:
			ContactFind(&con);
			break;
		case MODIFY:
			ContactMonify(&con);
			break;
		case SHOW:
			ContactShow(&con);
			break;
		case EXIT:
			ContactSave(&con);
			ContactDestroy(&con);

			printf("退出通讯录\n");
			break;
		default:
			printf("输入错误,请重新输入\n");
			break;
		}

	} while (input);
}

int main()
{
	contact();

	return 0;
}

结尾

之后就是简单数据结构了,再之后就是C艹和Linux和一些刷题的题解方面的博客。

可文件存储的动态通讯录(C语言)_第1张图片

 

你可能感兴趣的:(C语言,c语言)