通讯录(文件版)

2023-02-24 09:35:26

写程序前我对整体框架进行了分析,在写的过程中对功能不断进行扩充,此通讯录共经历了三版。


整体思路

功能分析具体如下:

功能实现:
在这里插入图片描述

第一版通讯录:为静态通讯录。实现要求的增、删、改、查、功能。
信息未录入时,已按最大容量1000申请内存,会存在浪费内存或内存不够用的问题。
第二版通讯录:为动态增长通讯录。对空间的使用进行了升级。为方便验证,刚开始默认可以存放3个人信息,当发现当前通讯录满时,我们进行扩容,每次增加2个空间。
第三版通讯录:文件版本通讯录。进行日志记录,关键信息写到文件中。添加功能7将数据保存到文件中,在程序进行内容释放前,再次调用,save函数防止遗忘保存。为了在每次重新跑程序时,将已存文件加载到通讯录中,重新定义通讯录初始化,读取已保存文件。

一、关键解析

1、枚举与switch语句的联合 使用枚举enum 与switch语句进行连接,增加代码的可读性。
为菜单书写功能函数时,不必在来回看每个数对应哪一个函数。

2、函数的定义与调用 写代码过程中发现删除、修改、展示都需查找功能,出现代码冗余。
为消除代码冗余,剥离了公共函数,定义成static函数,在本文件内部调用。

3、 自定义数据类型-结构体使用结构体将多个变量进行打包,方便赋值,调用。
在未使用typedef时每次写结构体变量,前面都要加struct,一不小心漏写,vs就进行报错,很麻烦而且语句较为拖沓。用typedef重命名后,后续代码进行书写时带来了方便。

4、动态内存分配 使用malloc(sizeof)函数进行动态内存的分配,在增加数据时,进行容量检测,如果容量不够进行扩容,若容量够用,正常进行数据的添加。
最后将动态开辟的内容进行释放。以此完成了静态通讯录向动态扩容通讯录的转变。

5、文件操作文件的保存与读写,将保存好的信息直接在通讯录初始化时进行加载,实现了记录功能。
具体实现,定义了一个加载函数,在初始化时进行读取文件,若文件存在,判断内存是否够用,不够用进行扩容,够用则进行加载。


二、具体实现

1.定义通讯录类型

代码如下:

typedef struct Person
{
	char name[MAX_NAME];
	int age;
	char sex[MAX_SEX];
	char tele[MAX_TELE];
	char addr[MAX_ADDR];
}Person;
//定义通讯录类型
typedef struct Contact
{
	struct Person* data;//存放用户信息
	int size;//记录当前已有元素个数
	int capacity;//记录通讯录当前最大容量
}Contact;

2.初始化通讯录

这里初始化过程中,对文件进行了加载,代码如下:

void InitContact(struct Contact* ps)
{
	ps->data = (struct Person*)malloc(Begin * sizeof(struct Person));
	if (ps->data == NULL)
	{
		return;
	}
	ps->size = 0;
	ps->capacity = Begin;

	//把文件中已存放的通讯录信息,加载到通讯录
	LoadContact(ps);
}
void LoadContact(Contact* ps)
{
	Person tmp = { 0 };
	FILE* pfRead = fopen("contact.txt", "rb");
	if (pfRead == NULL)
	{
		printf("LoadContact::%s\n", strerror(errno));
		return;
	}
	//读取文件,存放到通讯录
	while (fread(&tmp, sizeof(Person), 1, pfRead))
	{
		CheckCapacity(ps);
		ps->data[ps->size] = tmp;
		ps->size++;
	}

	fclose(pfRead);
	pfRead = NULL;

}

3.检查内存进行增容

代码如下:

CheckCapacity(struct Contact* ps)
{
	if (ps->size == ps->capacity)
	{
		//增容
		struct Person* ptr = realloc(ps->data, (ps->capacity + 2) * sizeof(struct Person));
		if (ptr != NULL)
		{
			ps->data = ptr;
			ps->capacity += 2;
			printf("增容成功\n");
		}
		else
		{
			printf("增容失败\n");
		}
	}
}

4.添加信息

代码如下:

void AddContact(struct Contact* ps)
{
	//检查当前通讯录容量
	//1.如果容量满了,增容
	//2.如果未满,无操作;
	CheckCapacity(ps);
	//增加数据
	printf("请输入名字>\n");
	//放到下标为size的data数组中
	scanf("%s", ps->data[ps->size].name);
	printf("请输入年龄>\n");
	scanf("%d", &(ps->data[ps->size].age));
	//年龄为整型,与其他几个数组不同所以应取地址。
	printf("请输入性别>\n");
	scanf("%s", ps->data[ps->size].sex);
	printf("请输入电话>\n");
	scanf("%s", ps->data[ps->size].tele);
	printf("请输入地址>\n");
	scanf("%s", ps->data[ps->size].addr);
	ps->size++;
	printf("添加成功\n");
}

5.增、删、查都会用 索引

代码复用如下:

void AddContact(struct Contact* ps)
{
	//检查当前通讯录容量
	//1.如果容量满了,增容
	//2.如果未满,无操作;
	CheckCapacity(ps);
	//增加数据
	printf("请输入名字>\n");
	//放到下标为size的data数组中
	scanf("%s", ps->data[ps->size].name);
	printf("请输入年龄>\n");
	scanf("%d", &(ps->data[ps->size].age));
	//年龄为整型,与其他几个数组不同所以应取地址。
	printf("请输入性别>\n");
	scanf("%s", ps->data[ps->size].sex);
	printf("请输入电话>\n");
	scanf("%s", ps->data[ps->size].tele);
	printf("请输入地址>\n");
	scanf("%s", ps->data[ps->size].addr);

	ps->size++;
	printf("添加成功\n");


}

6.保存文件

代码如下:

void SaveContact(Contact* ps)
{
	FILE* pfWrite = fopen("contact.txt", "wb");
	if (pfWrite == NULL)
	{
		printf("SaveContact::%s\n", strerror(errno));
		return;
	}
	//写通讯录中数据到文件中
	int i = 0;
	for (i = 0; i < ps->size; i++)
	{
		fwrite(&(ps->data[i]), sizeof(Person), 1, pfWrite);
	}
	fclose(pfWrite);
	pfWrite = NULL;
}
# 三、完整代码 ## Contect.h

代码如下

#pragma once
#define MAX_NAME 20
#define MAX_SEX 5
#define MAX_TELE 12
#define MAX_ADDR 30

#define MAX 1000
#define Begin 3//初始容量
//使用宏定义方便以后对代码的更改

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<errno.h>
enum option
{
	EXIT,//0
	ADD,//1
	DEL,
	SEARCH,
	MODIFY,
	SHOW,
	SORT,
	SAVE
};

typedef struct Person
{
	char name[MAX_NAME];
	int age;
	char sex[MAX_SEX];
	char tele[MAX_TELE];
	char addr[MAX_ADDR];
}Person;
//定义通讯录类型
typedef struct Contact
{
	struct Person* data;//存放用户信息
	int size;//记录当前已有元素个数
	int capacity;//记录通讯录当前最大容量
}Contact;

//进行函数声明
//初始化函数通讯录
void InitContact(struct Contact* ps);

//增加信息到通讯录
void AddContact(struct Contact* ps);

//打印通讯录到信息
void ShowContact(const struct Contact* ps);

//删除指定的联系人
void DelContact(struct Contact* ps);

//查找指定的联系人
void SearchContact(const struct Contact* ps);

//修改指定的联系人
void ModifyContact(struct Contact* ps);

//销毁通讯录,释放动态开辟的内容
void DestroyContact(Contact* ps);

//保存文件
void SaveContact(Contact* ps);

//加载文件中的信息到通讯录
void LoadContact(Contact* ps);

Contect.c

代码如下

#define  _CRT_SECURE_NO_WARNINGS 1
//实现函数功能
#include "Contect.h"

void InitContact(struct Contact* ps)
{
	ps->data = (struct Person*)malloc(Begin * sizeof(struct Person));
	if (ps->data == NULL)
	{
		return;
	}
	ps->size = 0;
	ps->capacity = Begin;

	//把文件中已存放的通讯录信息,加载到通讯录
	LoadContact(ps);
}
CheckCapacity(ps);//函数声明
void LoadContact(Contact* ps)
{
	Person tmp = { 0 };
	FILE* pfRead = fopen("contact.txt", "rb");
	if (pfRead == NULL)
	{
		printf("LoadContact::%s\n", strerror(errno));
		return;
	}
	//读取文件,存放到通讯录
	while (fread(&tmp, sizeof(Person), 1, pfRead))
	{
		CheckCapacity(ps);
		ps->data[ps->size] = tmp;
		ps->size++;
	}
	fclose(pfRead);
	pfRead = NULL;

}
CheckCapacity(struct Contact* ps)
{
	if (ps->size == ps->capacity)
	{
		//增容
		struct Person* ptr = realloc(ps->data, (ps->capacity + 2) * sizeof(struct Person));
		if (ptr != NULL)
		{
			ps->data = ptr;
			ps->capacity += 2;
			printf("增容成功\n");
		}
		else
		{
			printf("增容失败\n");
		}
	}
}
void AddContact(struct Contact* ps)
{
	//检查当前通讯录容量
	//1.如果容量满了,增容
	//2.如果未满,无操作;
	CheckCapacity(ps);
	//增加数据
	printf("请输入名字>\n");
	//放到下标为size的data数组中
	scanf("%s", ps->data[ps->size].name);
	printf("请输入年龄>\n");
	scanf("%d", &(ps->data[ps->size].age));
	//年龄为整型,与其他几个数组不同所以应取地址。
	printf("请输入性别>\n");
	scanf("%s", ps->data[ps->size].sex);
	printf("请输入电话>\n");
	scanf("%s", ps->data[ps->size].tele);
	printf("请输入地址>\n");
	scanf("%s", ps->data[ps->size].addr);
	ps->size++;
	printf("添加成功\n");
}
void ShowContact(const struct Contact* ps)
{
	if (ps->size == 0)
	{
		printf("通讯录为空\n");
	}
	else
	{
		int i = 0;
		printf("%-20s\t%-4s\t%-5s\t%-12s\t%-20s\n", "名字", "年龄", "性别", "电话", "地址");
		for (i = 0; i < ps->size; i++)
		{
			printf("%-20s\t%-4d\t%-5s\t%-12s\t%-20s\n",
				ps->data[i].name,
				ps->data[i].age,
				ps->data[i].sex,
				ps->data[i].tele,
				ps->data[i].addr);

		}
	}
}

static int FindByName(const struct Contact* ps, char name[MAX_NAME])
{
	int i = 0;
	for (i = 0; i < ps->size; i++)
	{
		if (0 == strcmp(ps->data[i].name, name))
		{
			return i;
		}
	}
	return -1;//找不到时
}

void DelContact(struct Contact* ps)
{
	int pos = 0;
	char name[MAX_NAME];
	printf("请输入要删除人的名字:>\n");
	scanf("%s", name);
	//1.查找要删除人的位置
	pos = FindByName(ps, name);//找到了返回元素所在下标。找不到返回-1
   //2.删除
	if (pos == -1)
	{
		printf("要删除的人不存在\n");
	}
	else
	{
		//删除数据
		int j = 0;
		for (j = pos; j < ps->size; j++)
		{
			ps->data[j] = ps->data[j + 1];
		}
		ps->size--;
		printf("删除成功\n");
	}
}

void SearchContact(const struct Contact* ps)
{
	int pos = 0;
	char name[MAX_NAME];
	printf("请输入想查找人的名字:>\n");
	scanf("%s", name);
	pos = FindByName(ps, name);
	if (pos == -1)
	{
		printf("要查找的人不存在\n");
	}
	else
	{
		printf("%-20s\t%-4s\t%-5s\t%-12s\t%-20s\n", "名字", "年龄", "性别", "电话", "地址");
		printf("%-20s\t%-4d\t%-5s\t%-12s\t%-20s\n",
			ps->data[pos].name,
			ps->data[pos].age,
			ps->data[pos].sex,
			ps->data[pos].tele,
			ps->data[pos].addr);
	}
}

void ModifyContact(struct Contact* ps)
{
	int pos = 0;
	char name[MAX_NAME];
	





							
  • 作者:猿手冰凉
  • 原文链接:https://blog.csdn.net/m0_53089597/article/details/119849013
    更新时间:2023-02-24 09:35:26