通讯录实现之进阶版将通讯录数据保存在文件中(完整代码)

我们在之前的博客中已经写过两版通讯录了:

第一版是用C语言实现了通讯录,但是通讯录的存储人数信息是固定的,用完就没有了

感兴趣的可以转到对应博客看一下,附带链接:第一版通讯录

第二版是在第一版的基础上动态开辟内存,使这个通讯录可以无限(只有内存足够大)存储人的信息

感兴趣的可以转到对应博客看一下,附带链接:第二版通讯录

今天我们要实现的是第三版,建立在第二版的基础上,将写入通讯录的人的信息保存在文件中

这里附带我们讲解文件操作的链接,不了解的小伙伴可以先去看一下文件操作:C语言之文件操作

我们前两版的通讯录只要结束了程序我们之前存入内存的数据就都被销毁了,是没办法保存下来的,下次运行程序我们还得重新存入这部分人的信息,这样是很不方便的。但是我们现在已经学习了文件操作这部分知识,我们现在可以做到把已经写入通讯录中人的信息保存到文件中,也就是硬盘中,当我们结束通讯录程序的代码执行,这些人的信息依旧是存在的,我们只要打开对应文件就可以看到,下次想要在存入人的信息,可以直接存入别人的信息,不用在存入已经存入的人的信息!

我们在这篇里是不会详细介绍它这样写的原理的,因为这部分讲解在实现第一版通讯录就已经说的很清楚了。

好,现在交代清楚了,我们话不多说,上代码!!!


目录

test .c

Contact.c

Contact.h

运行结果展示


test .c

//实现将通讯录数据写入文件中(硬盘中)实现数据的永久保存
//里面存放人的信息,包括姓名,年龄,性别
//实现的通讯录功能有:
//电话号码和家庭住址
//结合枚举,必要时要增容
//它包括以下功能
//1.增加联系人
//2.删除指定联系人
//3.查找指定联系人
//4.修改指定联系人
//5.显示所有联系人
//6.对所有联系人进行排序(按姓名)
//7.退出通讯录(这时要记得释放动态内存开辟的空间,避免内存泄漏

#include"contact.h"
//加入枚举,给下面Switch case语句一个提示,函数写到了通讯录的哪个功能
enum contact
{
	exitContact,
	addContact,
	delContact,
	showContact,
	sehContact,
	mofContact,
	sortContact,
};
void menu(void)
{
	printf("*******************************************\n");
	printf("**********     1.AddContact      **********\n");
	printf("**********     2.DelContact      **********\n");
	printf("**********     3.ShowContact     **********\n");
	printf("**********     4.SehContact      **********\n");
	printf("**********     5.MofContact      **********\n");
	printf("**********     6.SortContact     **********\n");
	printf("**********     0.ExitContact     **********\n");
	printf("*******************************************\n");
}
int main()
{
	int input = 0;
	Contact con;
	//初始化通讯录
	InitContact(&con);
	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case addContact:
			AddContact(&con);
			break;
		case delContact:
			DelContact(&con);
			break;
		case showContact:
			ShowContact(&con);
			break;
		case sehContact:
			SehContact(&con);
			break;
		case mofContact:
			MofContact(&con);
			break;
		case sortContact:
			SortContact(&con);
			break;
		case exitContact:
			//把通讯录数据存入文件中
			SaveContact(&con);
			//销毁通讯录,进行动态空间的释放
			DestoryContact(&con);
			break;
		default:
			printf("输入错误,请重新输入!\n");
			break;
		}
	} while (input);
	return 0;
}

Contact.c

#include"contact.h"

//空间增容函数
void check_capacity(Contact* pc)
{
	assert(pc);
	if (pc->sz == pc->capacity)
	{
		//通讯录已满,进行增容
		PeopInfo* str = (PeopInfo*)realloc(pc->data, (pc->capacity + INC_SZ) * sizeof(PeopInfo));
		if (str == NULL)
		{
			perror("realloc");
			return;
		}
		pc->data = str;
		pc->capacity += INC_SZ;//总容量增加
		printf("空间增容成功,可以继续添加联系人\n");
	}
}

//通过名字查找
int FindByName(const Contact* pc, char* Name)
{
	assert(pc);
	int i = 0;
	if (pc->sz == 0)
	{
		printf("通讯录为空,无法查找!");
		return;
	}
	for (i = 0; i < pc->sz; i++)
	{
		if (strcmp(Name, pc->data[i].Name) == 0)
			return i;
	}
	return -1;
}

//初始化这个通讯录,初始化为对应的值,每次打开通讯录上次存入文件的信息
//都不会被覆盖,增加一个函数功能,加载文件信息到通讯录
void InitContact(Contact* pc)
{
	assert(pc);
	//为通讯录存放人的信息的结构体分配初始空间,并把里面数据直接
	//初始化为0,calloc函数很合适
	PeopInfo* str = (PeopInfo*)calloc(DEFAULT_SZ, sizeof(PeopInfo));
	if (str == NULL)
	{
		perror("calloc");
		return;
	}
	pc->data = str;
	pc->sz = 0;
	pc->capacity = DEFAULT_SZ;
	//加载文件中的信息到通讯录
	LoadContact(pc);
}

//加载文件中的信息到通讯录
void LoadContact(Contact* pc)
{
	assert(pc);
	//从文件中读取通讯录数据
	FILE* pf = fopen("Contact.txt", "rb");
	if (pf == NULL)
	{
		perror("LoadContact");
		return;
	}
	//读数据
	PeopInfo tmp = { 0 };//存放读到的数据
	int i = 0;
	while (fread(&tmp,sizeof(PeopInfo),1,pf))
	{
		//增容问题
		check_capacity(pc);
		pc->data[i] = tmp;
		pc->sz++;
		i++;
	}
	//读取完毕,关闭文件
	fclose(pf);
	pf = NULL;
}

//把通讯录数据存入文件中
void SaveContact(Contact* pc)
{
	assert(pc);
	//写数据
	//以二进制打开文件写入
	FILE* pf = fopen("Contact.txt", "wb");
	if (pf == NULL)
	{
		perror("SaveContact");
		return;
	}
	//开始写入
	int i = 0;
	for (i = 0; i < pc->sz; i++)
	{
		//这里大家可以自己尝试一下,fprintf和fputs 是否可以写入数据
		fwrite(pc->data + i, sizeof(PeopInfo), 1, pf);
	}
	//关闭文件
	fclose(pf);
	pf = NULL;
	printf("保存数据成功\n");
}

//增加通讯录联系人
void AddContact(Contact* pc)
{
	assert(pc);
	check_capacity(pc);//检查通讯录是否需要增容
	//增加人的信息
	printf("请输入联系人姓名:>");
	scanf("%s", pc->data[pc->sz].Name);
	printf("请输入联系人年龄:>");
	scanf("%d", &pc->data[pc->sz].Age);
	printf("请输入联系人性别:>");
	scanf("%s", pc->data[pc->sz].Sex);
	printf("请输入联系人电话号码:>");
	scanf("%s", pc->data[pc->sz].Tele);
	printf("请输入联系人家庭住址:>");
	scanf("%s", pc->data[pc->sz].Addr);
	pc->sz++;
}
//删除指定联系人
void DelContact(Contact* pc)
{
	assert(pc);
	char Name[NAME_MAX] = { 0 };
	int i = 0;
	if (pc->sz == 0)
	{
		printf("通讯录为空,无法删除!\n");
		return;
	}
	printf("请输入要删除的人的姓名:>");
	scanf("%s", Name);
	int pos = FindByName(pc, Name);
	if (pos == -1)
	{
		printf("要删除的联系人不存在!\n");
		return;
	}
	for (i = pos; i < pc->sz - 1; i++)
	{
		pc->data[i] = pc->data[i + 1];
	}
	pc->sz--;
	printf("删除联系人成功!\n");
}

//显示所有联系人
void ShowContact(const Contact* pc)
{
	assert(pc);
	if (pc->sz == 0)
	{
		printf("通讯录为空,无法显示!\n");
		return;
	}
	int i = 0;
	printf("%-20s\t%-4s\t%-5s\t%-12s\t%-30s\n", "姓名", "年龄", "性别", "电话号码", "家庭住址");
	for (i = 0; i < pc->sz; i++)
	{
		printf("%-20s\t%-4d\t%-5s\t%-12s\t%-30s\n",
			pc->data[i].Name,
			pc->data[i].Age,
			pc->data[i].Sex,
			pc->data[i].Tele,
			pc->data[i].Addr);
	}
}

//查找指定联系人
void SehContact(const Contact* pc)
{
	assert(pc);
	if (pc->sz == 0)
	{
		printf("通讯录为空,无法查找!\n");
		return;
	}
	char Name[NAME_MAX] = { 0 };
	printf("请输入要查找的人的姓名:>");
	scanf("%s", Name);
	int pos = FindByName(pc, Name);
	if (pos == -1)
	{
		printf("要查找的联系人不存在!\n");
		return;
	}
	printf("%-20s\t%-4s\t%-5s\t%-12s\t%-30s\n", "姓名", "年龄", "性别", "电话号码", "家庭住址");
	printf("%-20s\t%-4d\t%-5s\t%-12s\t%-30s\n",
		pc->data[pos].Name,
		pc->data[pos].Age,
		pc->data[pos].Sex,
		pc->data[pos].Tele,
		pc->data[pos].Addr);
}
//修改指点联系人的信息
void MofContact(Contact* pc)
{
	assert(pc);
	if (pc->sz == 0)
	{
		printf("通讯录为空,无法修改!\n");
		return;
	}
	char Name[NAME_MAX] = { 0 };
	printf("请输入要修改的人的姓名:>");
	scanf("%s", Name);
	int pos = FindByName(pc, Name);
	if (pos == -1)
	{
		printf("要修改的联系人不存在!\n");
		return;
	}
	printf("请输入联系人姓名:>");
	scanf("%s", pc->data[pos].Name);
	printf("请输入联系人年龄:>");
	scanf("%d", &pc->data[pos].Age);
	printf("请输入联系人性别:>");
	scanf("%s", pc->data[pos].Sex);
	printf("请输入联系人电话号码:>");
	scanf("%s", pc->data[pos].Tele);
	printf("请输入联系人家庭住址:>");
	scanf("%s", pc->data[pos].Addr);
}

//对所有联系人按姓名进行排序
void SortContact(const Contact* pc)
{
	assert(pc);
	int i = 0;
	int j = 0;
	for (i = 0; i < pc->sz; i++)
	{
		for (j = i; j < pc->sz; j++)
		{
			if (strcmp(pc->data[i].Name, pc->data[j].Name) > 0)
			{
				PeopInfo temp[] = { 0 };
				temp[0] = pc->data[i];
				pc->data[i] = pc->data[j];
				pc->data[j] = temp[0];
			}
		}
	}
	ShowContact(pc);//显示一下排列结果
}

//销毁通讯录,释放动态内存空间
void DestoryContact(Contact* pc)
{
	free(pc->data);
	pc->data = NULL;
	pc->sz = 0;
	pc->capacity = 0;
	pc = NULL;
	printf("通讯录已退出\n");
}

Contact.h

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<assert.h>


#define DEFAULT_SZ 3 //初始通讯录内存放的联系人个数
#define INC_SZ 2     //每次增容的空间
#define NAME_MAX 30
#define SEX_MAX 5
#define TELE_MAX 12
#define ADDR_MAX 30



//创建结构体存放有关人的信息
typedef struct PeopInfo
{
	char Name[NAME_MAX];
	int Age;
	char Sex[SEX_MAX];
	char Tele[TELE_MAX];
	char Addr[ADDR_MAX];
}PeopInfo;

//创建初始通讯录,里面存放人的信息,初始内存大小
typedef struct Contact
{
	PeopInfo* data;  //存放人的信息
	int sz;
	int capacity;   //初始内存
}Contact;

//空间增容函数
void check_capacity(Contact* pc);

//初始化这个通讯录,初始化为对应的值
void InitContact(Contact* pc);
//增加通讯录联系人
void AddContact(Contact* pc);
//删除指定联系人
void DelContact(Contact* pc);
//显示所有联系人
void ShowContact(const Contact* pc);
//通过名字查找
int FindByName(const Contact* pc, char* Name);
//查找指定联系人
void SehContact(const Contact* pc);
//修改指点联系人的信息
void MofContact(Contact* pc);
//对所有联系人按姓名进行排序
void SortContact(const Contact* pc);

//把通讯录数据存入文件中
void SaveContact(Contact* pc);

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

//销毁通讯录,释放动态内存空间
void DestoryContact(Contact* pc);

运行结果展示

程序代码上的显示(已经存入数据)

在文件中的显示


总体的代码实现就是这样了,希望大家可以理解哟!

我们下期再见!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/155298.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

从零开始 通义千问大模型本地化到阿里云通义千问API调用

从零开始 通义千问大模型本地化到阿里云通义千问API调用 一、通义千问大模型介绍 何为“通义千问”&#xff1f; “通义千问大模型”是阿里云推出的一个超大规模的语言模型&#xff0c;具有强大的归纳和理解能力&#xff0c;可以处理各种自然语言处理任务&#xff0c;包括但…

DAC实验(DAC 输出三角波实验)(DAC 输出正弦波实验)

DAC 输出三角波实验 本实验我们来学习使用如何让 DAC 输出三角波&#xff0c;DAC 初始化部分还是用 DAC 输出实验 的&#xff0c;所以做本实验的前提是先学习 DAC 输出实验。 使用 DAC 输出三角波&#xff0c;通过 KEY0/KEY1 两个按键&#xff0c;控制 DAC1 的通道 1 输出两种…

文旅媒体有哪些?如何邀请到现场报道?

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。 中国文旅产业在近年来得到了持续而快速的发展。从产业端看&#xff0c;中国文旅产业呈现出新的发展趋势&#xff0c;其中“文旅”向“文旅”转变成为显著特点。通过产业升级和空间构建&a…

Shell编程基础(3)- Shell的位置参数

Shell编程基础&#xff08;3&#xff09;- Shell的位置参数 Shell Scripting Essentials (3) – Locative Parameters of Shell Scripting 前文介绍过shell变量。当声明shell变量时&#xff0c;只需要在代码行写出变量名称即可;在输入行用read命令要求用户输入&#xff0c;在…

Day48 力扣动态规划 : 647. 回文子串 |516.最长回文子序列 |动态规划总结篇

Day48 力扣动态规划 : 647. 回文子串 &#xff5c;516.最长回文子序列 &#xff5c;动态规划总结篇 647. 回文子串第一印象看完题解的思路dp递推公式初始化递归顺序 实现中的困难感悟代码 516.最长回文子序列第一印象我的尝试遇到的问题 看完题解的思路dp递推公式初始化 实现中…

设计基于STM32F103C8T6微控制器的巡线小车

巡线小车是一种能够在一条预定线追踪路径的小车&#xff0c;广泛应用于工业自动化、物流仓储、智能家居等领域。本设计将使用STM32F103C8T6微控制器来实现一个基础的巡线小车。 硬件组成&#xff1a;1. STM32F103C8T6微控制器开发板&#xff1a;作为巡线小车的核心控制器&…

双剑合璧:基于Elasticsearch的两路召回语义检索系统,实现关键字与语义的高效精准匹配

搜索推荐系统专栏简介:搜索推荐全流程讲解(召回粗排精排重排混排)、系统架构、常见问题、算法项目实战总结、技术细节以及项目实战(含码源) 专栏详细介绍:搜索推荐系统专栏简介:搜索推荐全流程讲解(召回粗排精排重排混排)、系统架构、常见问题、算法项目实战总结、技术…

NewStarCTF2023 Reverse Week3 EzDLL WP

分析 这里调用了z3h.dll中的encrypt函数。 用ida64载入z3h.dll 直接搜索encrypt 找到了一个XTEA加密。接着回去找key和密文。 发现key 这里用了个调试状态来判断是否正确&#xff0c;v71&#xff0c;要v7&#xff1d;1才会输出Right&#xff0c;即程序要处于飞调试状态。 可…

asp.net core EF Sqlserver

一、EF CORE的使用 1、使用NuGet来安装EF CORE 使用程序包管理器控制台&#xff0c;进行命令安装 //安装 Microsoft.EntityFrameworkCoreInstall-Package Microsoft.EntityFrameworkCore //安装 Microsoft.EntityFrameworkCore.SqlServer Install-Package Microsoft.EntityF…

Java智慧工地云SaaS源码,AI服务器、智能硬件

智慧工地智能硬件 一、自动喷淋控制 当扬尘监测值超过在智慧工地系统中设定的闽值后自动喷淋控制系统通过接收系统发出的开关指令&#xff0c;实现自动、及时喷淋降尘&#xff0c;同时系统可设置自动喷淋时间段&#xff0c;每天定时喷淋&#xff0c;避免环境污染。 二、智能电…

采用Nexus搭建Maven私服

采用Nexus搭建Maven私服 1.采用docker安装 1.创建数据目录挂载的目录&#xff1a; /usr/local/springcloud_1113/nexus3/nexus-data2.查询并拉取镜像docker search nexus3docker pull sonatype/nexus33.查看拉取的镜像docker images4.创建docker容器&#xff1a;可能出现启动…

【vue】下载导出excel

下载导出excel 首先使用的tdesign框架&#xff0c;要导出后端返回的数据流excel 遇见的问题 下载的文件&#xff0c;里边的内容是undefined 观察报错 一看就知道并不是后端的报错&#xff0c;后端不可能是undefined 在强烈的好奇心驱动下&#xff0c;看了下接口&#xff0…

Docker安装MinIO遇到的(汇总——持续更新中)

文章目录 Docker安装MinIO遇到的坑前言问题1&#xff1a;执行docker run报错Error response from daemon问题2&#xff1a;启动MinIO容器浏览器无法访问问题3&#xff1a;上传文件报错InvalidResponseException问题4&#xff1a;上传文件报错Connection refused最终的启动指令问…

【Electron】electron-builder打包失败问题记录

文章目录 yarn下载的包不支持require()winCodeSign-2.6.0.7z下载失败nsis-3.0.4.1.7z下载失败待补充... yarn下载的包不支持require() 报错内容&#xff1a; var stringWidth require(string-width)^ Error [ERR_REQUIRE_ESM]: require() of ES Module /stuff/node_modules/…

轮播图(多个一起轮播)

效果图 class MainActivity : Activity(), Runnable {private lateinit var viewPager: ViewPagerprivate lateinit var bannerAdapter: BannerAdapterprivate val images ArrayList<Int>() // 存储图片资源的列表private val handler Handler() // 用于定时发送消息…

Linux磁盘分区快速上手(讲解详细)

一、磁盘分区 在Linux中&#xff0c;磁盘是通过分区来使用的。分区是将一个硬盘划分成几个逻辑部分来使用&#xff0c;在每个分区中可以存储不同的文件系统。因此&#xff0c;在挂载磁盘之前&#xff0c;我们需要先对磁盘进行分区。磁盘分区的过程可以通过命令行工具或图形界面…

Unity中Shader矩阵的转置矩阵

文章目录 前言一、转置的表示二、转置矩阵三、转置矩阵的总结1、(A^T^)^T^ A2、(A B)^T^ A^T^ B^T^3、(kA)^T^ kA^T^ (k为实数)4、(AB)^T^ B^T^A^T^5、如果 A A^T^ 则称A为对称矩阵6、如果 AA^T^ I(单位矩阵)&#xff0c;则称 A 为正交矩阵&#xff0c;同时 A^T^ A^-1…

OpenCV中的像素重映射原理及实战分析

引言 映射是个数学术语&#xff0c;指两个元素的集之间元素相互“对应”的关系&#xff0c;为名词。映射&#xff0c;或者射影&#xff0c;在数学及相关的领域经常等同于函数。 基于此&#xff0c;部分映射就相当于部分函数&#xff0c;而完全映射相当于完全函数。 说的简单点…

MySQL 1、初识数据库

一、什么是数据库&#xff1f; 以特定的格式保存好的文件&#xff0c;我们就叫做数据库。 提供较为便捷的数据的存取服务的软件集合、解决方案&#xff0c;我们就叫它数据库。 存储数据用文件就可以了&#xff0c;为什么还要弄个数据库。 文件或数据库都可以存储数据&#…

react+video.js h5自定义视频暂停图标

目录 参考网址 效果图&#xff0c;暂停时显示暂停图标&#xff0c;播放时隐藏暂停图标 代码说明&#xff0c;代码传入url后&#xff0c;可直接复制使用 VideoPausedIcon.ts 组件 VideoCom.tsx Video.module.less 参考网址 在Video.js播放器中定制自己的组件 - acgtofe 效…