[C]实现能在本地存储的简易通讯录

news2024/11/24 11:50:27

作者华丞臧.
专栏【C语言】
各位读者老爷如果觉得博主写的不错,请诸位多多支持(点赞+收藏+关注)。如果有错误的地方,欢迎在评论区指出。


文章目录

  • 一、文件动态通讯录
  • 二、相关代码解析
    • 2.1 初始化
    • 2.2 销毁通讯录
    • 2.3 增加联系人
    • 2.4 查找指定联系人
    • 2.5 删除指定联系人
    • 2.6 修改指定联系人
    • 2.7 通讯录清空
    • 2.8 按照姓名排序
  • 三、代码
    • 3.1 main.c
    • 3.2 Contact.h
    • 3.3 Contact.c


一、文件动态通讯录

前面写过一个静态通讯录,静态通讯录的容量是固定的,存放联系人达到最大容量就不能再存放了,并且在程序运行结束通讯录就销毁了;静态通讯录缺点很多,既不能根据用户需求扩容又不能在本地保存。

这次在学习相关文件操作后,我们来尝试写一个可以再本地文件保存数据的可动态增长的通讯录。

二、相关代码解析

2.1 初始化

初始化函数不仅要初始化data数组,还需要把本地保存的联系人数据加载到通讯录当中;而加载数据到data数组中去需要我们在堆上开辟空间,并且我们并不知道本地存储了多少字节的数据,所以写一个检查扩容的函数,当data数组没有开辟内存或者存满了就扩容,一次扩容两倍。

使用的是fread库函数,其函数原型如下:
在这里插入图片描述

//检查扩容
void CheckContactCapacity(CT* con)
{
	assert(con);

	//判断是否需要扩容
	if (con->size == con->capacity)
	{
		int newCapacity = con->capacity * 2;
		PeoInfo* tmp = (PeoInfo*)realloc(con->data, newCapacity * sizeof(PeoInfo));
		if (tmp == NULL)
		{
			perror("realloc fail");
			exit(-1);
		}
		con->data = tmp;
		con->capacity = newCapacity;
		printf("扩容成功\n");
	}	
}


//初始化
void ContactInit(CT* con)
{
	assert(con);
	
	con->data = (PeoInfo*)malloc(4 * sizeof(PeoInfo));
	con->size =  0;
	con->capacity = 4;
	//memset(con->data, 0, con->capacity * sizeof(PeoInfo));

	FILE* pf = fopen("contact.txt", "rb");
	PeoInfo s = { 0 };
	while (fread(&s, sizeof(PeoInfo), 1, pf))
	{
		CheckContactCapacity(con);
		con->data[con->size] = s;
		con->size++;
	}
	fclose(pf);
}

2.2 销毁通讯录

销毁通讯录,把通讯录当中的数据写入到本地文件当中并且释放动态内存开辟的空间。
在这里我使用的是fwrite库函数,其函数原型如下:
在这里插入图片描述

//通讯录销毁
void ContactDestroy(CT* con)
{
	assert(con);
	if (con->size != 0)
	{
		FILE* pf = fopen("contact.txt", "wb");

		int i = 0;
		PeoInfo s = { 0 };
		while (i < con->size)
		{
			s = con->data[i];
			fwrite(&s, sizeof(PeoInfo), 1, pf);
			i++;
		}

		fclose(pf);
	}

	free(con->data);
	con->data = NULL;
	con->size = con->capacity = 0;
}

2.3 增加联系人

通讯录是用来存储联系人信息的,那么就必然需要等一系列接口,通过这些接口来对通讯录进行操作。那么在通讯录中增加一个新的联系人,首先需要判断是否需要扩容;在初始化函数当中已经写了CheckContactCapacity()的函数直接调用即可,然后输入要增加的人的信息依次输入,用scanf函数存入对应的data数组当中。

//增加
void ContactAdd(CT* con)
{
	assert(con);
	
	//判断是否需要扩容
	CheckContactCapacity(con);
	
	//输入联系人信息
	printf("请输入姓名->");
	scanf("%s",con->data[con->size].name);

	printf("请输入性别->");
	scanf("%s", con->data[con->size].sex);

	printf("请输入年龄->");
	scanf("%d", &(con->data[con->size].age));

	printf("请输入电话->");
	scanf("%s", con->data[con->size].tele);

	printf("请输入地址->");
	scanf("%s", con->data[con->size].addr);
	
	con->size++;
}

2.4 查找指定联系人

查找指定联系人,需要知道该联系人至少一个信息。既然是按照姓名查找,就写一个FindByName()函数用来查找联系人,找到了返回下标找不到返回-1;找到了,就根据返回的下标把其对应的信息打印出来。


//按照名字查找,找到返回下标,找不到返回-1
int FindByName(CT* con,char* name)
{
	assert(con);
	
	PeoInfo* cur = con->data;

	//遍历查找
	int i = 0;
	while (i < con->size)
	{
		if (strcmp(name, (cur + i)->name) == 0)
		{
			return i;  //
		}
		i++;
	}
	return -1;
}

//查找
void ContactFind(CT* con)
{
	assert(con);

	char* name[NAME_MAX] = { 0 };

	printf("请输入要查找人的姓名->");
	scanf("%s",name);

	int n = FindByName(con,name);
	if (-1 == n)
	{
		printf("查找的人不存在\n");
		return;
	}

	printf("%-20s\t %-5s\t %-3s\t %-12s\t %-30s\t\n", 
		"姓名", 
		"性别", 
		"年龄", 
		"电话", 
		"地址");

	printf("%-20s\t %-5s\t %-3d\t %-12s\t %-30s\t\n"
		, con->data[n].name
		, con->data[n].sex
		, con->data[n].age
		, con->data[n].tele
		, con->data[n].addr);
}

2.5 删除指定联系人

通过姓名删除指定联系人,调用FindByName()函数找出要删除的人的下标,再挪动数据data数组处于下标位置的数据就可以达到删除的效果。

//删除
void ContactDel(CT* con)
{
	assert(con);
	assert(con->size);

	char name[NAME_MAX] = { 0 };

	printf("请输入要删除人的姓名->");
	scanf("%s",name);

	//查找
	int  n = FindByName(con, name);
	if (-1 == n)
	{
		printf("删除的人不存在\n");
	}
	else
	{
		//挪动数据
		while (n < con->size - 1)
		{
			con->data[n] = con->data[n + 1];
			n++;
		}

		con->size--;
	}
	printf("删除成功\n");
}

2.6 修改指定联系人

这里也是通过姓名来查找并且修改联系人的信息,同样调用FindByName()函数找出要修改的练习人的下标,通过返回的下标对其进行修改操作。

//修改
void ContactModify(CT* con)
{
	assert(con);

	char* name[NAME_MAX] = { 0 };

	printf("请输入要修改人的姓名->");
	scanf("%s",name);

	int n = FindByName(con, name);
	if (-1 == n)
	{
		printf("要修改的人不存在\n");
		return;
	}

	//输入联系人信息
	printf("请输入姓名->");
	scanf("%s", con->data[n].name);

	printf("请输入性别->");
	scanf("%s", con->data[n].sex);

	printf("请输入年龄->");
	scanf("%d", &(con->data[n].age));

	printf("请输入电话->");
	scanf("%s", con->data[n].tele);

	printf("请输入地址->");
	scanf("%s", con->data[n].addr);

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

2.7 通讯录清空

通讯录清空只需要把con->size置为0即可,con->size表示data数组中有效数据的个数。

//清空
void ContactClear(CT* con)
{
	assert(con);

	con->size = 0;
	
	printf("清空成功\n");
}

2.8 按照姓名排序

利用qsort函数,按照姓名对data中存放的数据进行排序;这里需要我们写一个比较字符串大小的函数,而strcmp()是用来比较字符串大小的库函数,且其返回值与qsort的要求一致。
qsort的说明和使用可见👉:qsort的使用和实现

int cmp_by_name(char* str1, char* str2)
{
	return strcmp(str1, str2);
}

//按照姓名排序
void ContactSortByName(CT* con)
{
	assert(con);

	qsort(con->data, con->size, sizeof(PeoInfo), cmp_by_name);

	ContactShow(con);
}

三、代码

3.1 main.c

#include "Contact.h"

void menu()
{
	printf("***** ***** ***** ***** *****\n");
	printf("***** 0.退出     1.添加 *****\n");
	printf("***** 2.删除     3.查找 *****\n");
	printf("***** 4.修改     5.展示 *****\n");
	printf("***** 6.排序     7.清空 *****\n");
	printf("***** ***** ***** ***** *****\n");
}

int main()
{
	
	CT con;
	ContactInit(&con);

	int input = 0;
	do
	{
		menu();
		printf("请选择->");
		scanf("%d",&input);
		switch (input)
		{
		case EXIT:
			ContactDestroy(&con);
			break;
		case ADD:
			ContactAdd(&con);
			break;
		case DEL:
			ContactDel(&con);
			break;
		case SEARCH:
			ContactSearch(&con);
			break;
		case MODIFY:
			ContactModify(&con);
			break;
		case SHOW:
			ContactShow(&con);
			break;
		case SORT:
			ContactSortByName(&con);
			break;
		case CLEAR:
			ContactClear(&con);
			break;
		default:
			break;
		}
	} while (input);
	return 0;
}

3.2 Contact.h

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


#define NAME_MAX 20
#define SEX_MAX 5
#define TELE_MAX 12
#define ADDR_MAX 30


typedef struct PeopleInfo
{
	char name[NAME_MAX];
	char sex[SEX_MAX];
	int age;
	char* tele[TELE_MAX];
	char* addr[ADDR_MAX];
}PeoInfo;

typedef struct Contact
{
	PeoInfo* data;
	int size;     //联系人个数
	int capacity; //容量
}CT;

enum Operations
{
	EXIT,
	ADD,
	DEL,
	SEARCH,
	MODIFY,
	SHOW,
	SORT,
	CLEAR
};

//初始化
void ContactInit(CT* con);

//打印通讯录信息
void ContactShow(CT* con);

//增加
void ContactAdd(CT* con);

//删除
void ContactDel(CT* con);

//查找
void ContactSearch(CT* con);

//修改
void ContactModify(CT* con);

//清空
void ContactClear(CT* con);

//按照姓名排序
void ContactSortByName(CT* con);

//通讯录销毁
void ContactDestroy(CT* con);

3.3 Contact.c

#include "Contact.h"

//检查扩容
void CheckContactCapacity(CT* con)
{
	assert(con);

	//判断是否需要扩容
	if (con->size == con->capacity)
	{
		int newCapacity = con->capacity * 2;
		PeoInfo* tmp = (PeoInfo*)realloc(con->data, newCapacity * sizeof(PeoInfo));
		if (tmp == NULL)
		{
			perror("realloc fail");
			exit(-1);
		}
		con->data = tmp;
		con->capacity = newCapacity;
		//memset(tmp+con->size, 0, (con->capacity-con->size) * sizeof(PeoInfo));
		printf("扩容成功\n");
	}	
}


//初始化
void ContactInit(CT* con)
{
	assert(con);
	
	con->data = (PeoInfo*)malloc(4 * sizeof(PeoInfo));
	con->size =  0;
	con->capacity = 4;
	//memset(con->data, 0, con->capacity * sizeof(PeoInfo));

	FILE* pf = fopen("contact.txt", "rb");
	PeoInfo s = { 0 };
	while (fread(&s, sizeof(PeoInfo), 1, pf))
	{
		CheckContactCapacity(con);
		con->data[con->size] = s;
		con->size++;
	}

	fclose(pf);

}
//打印通讯录信息
void ContactShow(CT* con)
{
	assert(con);

	PeoInfo* cur = con->data;

	//遍历打印
	printf("%-20s\t %-5s\t %-3s\t %-12s\t %-30s\t\n", "姓名", "性别", "年龄", "电话", "地址");
	int i = 0;
	while (i < con->size)
	{
		printf("%-20s\t %-5s\t %-3d\t %-12s\t %-30s\t\n", con->data[i].name
		, con->data[i].sex
		, con->data[i].age
		, con->data[i].tele
		, con->data[i].addr);
		i++;
	}
}

//增加
void ContactAdd(CT* con)
{
	assert(con);
	
	//判断是否需要扩容
	CheckContactCapacity(con);
	
	
	//输入联系人信息
	printf("请输入姓名->");
	scanf("%s",con->data[con->size].name);

	printf("请输入性别->");
	scanf("%s", con->data[con->size].sex);

	printf("请输入年龄->");
	scanf("%d", &(con->data[con->size].age));

	printf("请输入电话->");
	scanf("%s", con->data[con->size].tele);

	printf("请输入地址->");
	scanf("%s", con->data[con->size].addr);
	
	con->size++;
}

//按照名字查找,找到返回下标,找不到返回-1
int FindByName(CT* con,char* name)
{
	assert(con);
	
	PeoInfo* cur = con->data;

	//遍历查找
	int i = 0;
	while (i < con->size)
	{
		if (strcmp(name, cur->name) == 0)
		{
			return i;  //
		}
		i++;
	}
	return -1;
}

//删除
void ContactDel(CT* con)
{
	assert(con);
	assert(con->size);

	char name[NAME_MAX] = { 0 };

	printf("请输入要删除人的姓名->");
	scanf("%s",name);

	//查找
	int  n = FindByName(con, name);
	if (-1 == n)
	{
		printf("删除的人不存在\n");
	}
	else
	{
		//挪动数据
		while (n < con->size - 1)
		{
			con->data[n] = con->data[n + 1];
			n++;
		}

		con->size--;
	}
	printf("删除成功\n");
}


//查找
void ContactSearch(CT* con)
{
	assert(con);

	char* name[NAME_MAX] = { 0 };

	printf("请输入要查找人的姓名->");
	scanf("%s",name);

	int n = FindByName(con,name);
	if (-1 == n)
	{
		printf("查找的人不存在\n");
		return;
	}

	printf("%-20s\t %-5s\t %-3s\t %-12s\t %-30s\t\n", 
		"姓名", 
		"性别", 
		"年龄", 
		"电话", 
		"地址");

	printf("%-20s\t %-5s\t %-3d\t %-12s\t %-30s\t\n"
		, con->data[n].name
		, con->data[n].sex
		, con->data[n].age
		, con->data[n].tele
		, con->data[n].addr);
}

//修改
void ContactModify(CT* con)
{
	assert(con);

	char* name[NAME_MAX] = { 0 };

	printf("请输入要修改人的姓名->");
	scanf("%s",name);

	int n = FindByName(con, name);
	if (-1 == n)
	{
		printf("要修改的人不存在\n");
		return;
	}
	
		//输入联系人信息
		printf("请输入姓名->");
		scanf("%s", con->data[n].name);

		printf("请输入性别->");
		scanf("%s", con->data[n].sex);

		printf("请输入年龄->");
		scanf("%d", &(con->data[n].age));

		printf("请输入电话->");
		scanf("%s", con->data[n].tele);

		printf("请输入地址->");
		scanf("%s", con->data[n].addr);
		printf("修改成功\n");
}


//清空
void ContactClear(CT* con)
{
	assert(con);

	con->size = 0;

	printf("清空成功\n");
}

int cmp_by_name(char* str1, char* str2)
{
	return strcmp(str1, str2);
}

//按照姓名排序
void ContactSortByName(CT* con)
{
	assert(con);

	qsort(con->data, con->size, sizeof(PeoInfo), cmp_by_name);

	ContactShow(con);
}


//通讯录销毁
void ContactDestroy(CT* con)
{
	assert(con);
	if (con->size != 0)
	{
		FILE* pf = fopen("contact.txt", "wb");

		int i = 0;
		PeoInfo s = { 0 };
		while (i < con->size)
		{
			s = con->data[i];
			fwrite(&s, sizeof(PeoInfo), 1, pf);
			i++;
		}

		fclose(pf);
	}

	free(con->data);
	con->data = NULL;
	con->size = con->capacity = 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

NLP模型(一)——word2vec实现

文章目录1. 整体思路2. 数据处理3. 数据准备4. 创建数据管道5. 构建模型6. 模型训练7. 加载模型得到词向量8. 总结前面我介绍了word2vec算法的两种实现算法&#xff0c;Skip−gramSkip-gramSkip−gram 以及 CBOWCBOWCBOW 算法&#xff0c;我认为理解一个算法最好的方法就是复现…

stft的窗函数设计要求和方法(COLA)

在语音处理进行短时傅里叶变换的时候&#xff0c;对窗函数是有一定要求的&#xff0c;这篇文章将对这方面的问题进行简单的阐述。 一、背景描述 常用的语音处理需要进行这样处理: stft分帧会对信号产生截断&#xff0c;为尽可能避免这种影响&#xff0c;应考虑考虑加合适的窗 …

互融云借条APP系统开发 六大系统优势全面保障

借条是指借个人或公家的现金或物品时写给对方的条子。它是一种凭证性文书&#xff0c;通常用于日常生活以及商业管理方面。借条的本质就是借款合同&#xff0c;只不过形式比较简单&#xff0c;那么电子借条也就是简单的电子借款合同。与传统的纸质合同相比&#xff0c;电子借条…

目标检测算法——YOLOv5/YOLOv7改进之结合无参注意力SimAM(涨点神器)

目录 &#xff08;一&#xff09;前言介绍 1.摘要 2.不同注意力步骤比较 &#xff08;二&#xff09;相关实验 &#xff08;三&#xff09;YOLOv5结合无参注意力SimAM 1.配置.yaml文件 2.配置common.py 3.修改yolo.py SimAM&#xff1a;无参数Attention助力分类/检测/分…

想带着学生做一个操作系统,可行性有多大?

有知乎网友提问如下: 想带着学生做一个操作系统&#xff0c;可行性有多大&#xff1f; 个人觉得可行性非常大&#xff0c;如果只是做着来玩&#xff0c;让学生了解操作系统时如何实现的话。但是&#xff0c;如果你打算今后商业化的话&#xff0c;那就另当别论了。就算你能做出来…

单片机实验——水塔自动抽水系统设计(基于Proteus仿真)

实验内容及要求 自来水供水是现代生活的一大特点&#xff0c;水塔作为储水装置是自来水系统必不可少的重要设施&#xff0c;让水塔保持一定的水量是自来水不断供的必要条件&#xff0c;本设计模拟自来水系统中水塔的自动抽水机制&#xff0c;设计分为控制系统和虚拟水塔两部分…

若依管理框架-漏洞复现

文章目录 0x00 介绍0x01 默认口令漏洞0x02 SQL注入0x03 Shiro反序列化漏洞0x04 任意文件读取/下载0x05 定时任务0x06 `swagger-ui.html`接口文档泄漏0x07 Druid未授权访问摘抄免责声明0x00 介绍 RuoYi 是一个 Java EE 企业级快速开发平台,基于经典技术组合(Spring Boot、Apa…

黑产工具情报的分析方式浅析

接下来我们以恶意爬虫、抢券工具和注册机三种工具来谈一下黑产工具情报的分析方式。 对于企业方面来说&#xff0c;黑产工具情报可以有效的提高业务安全的攻防效率。通过分析工具利用的业务接口&#xff0c;不仅可以将黑产作恶行为进行有效的追踪&#xff0c;对其进行有效的处…

Java本地搭建宝塔部署实战springboot自动化立体智慧仓库WMS源码

大家好啊&#xff0c;我是测评君&#xff0c;欢迎来到web测评。 本期给大家带来一套Java开发的springboot自动化立体智慧仓库WMS源码。 技术架构 技术框架&#xff1a;SpringBoot layui HTML CSS JS运行环境&#xff1a;jdk8 IntelliJ IDEA maven3 宝塔面板 本地搭建教…

MongoDB入门与实战-第四章-SpringBoot集成MongoDB

目录参考MongoDB 连接java客户端方式引入驱动依赖测试创建客户端创建集合查询文档查询集合大小条件查询排序投影聚合查询复合聚合插入文档批量插入更新文档删除文档SpringDataMongoDB添加依赖配置文件新建实体映射插入文档修改文档删除参考 SpringBoot 整合 MongoDB 实战解说 …

[附源码]java毕业设计濒危物种科普系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

jq获取和设置偏移量:offset()、position()

jq获取和设置偏移量&#xff1a; js获取盒子的偏移量是&#xff1a;offsetLeft、offsetTop&#xff1b;jq获取盒子的偏移量的方法&#xff1a;offset&#xff08;&#xff09;、position&#xff08;&#xff09;&#xff1b;offset&#xff08;&#xff09;&#xff1a;距离文…

去除有重复的行

【问题】 I have a csv file and I have duplicate as well as unique data getting add to it on a daily basis. This involves too many duplicates. I have to remove the duplicates based on specific columns. For eg: csvfile1: title1 title2 title3 title4 title5…

C++程序开启大地址(虚拟内存),让32位程序使用4G内存的方,虚拟内存概念及寻址范围详解

如何让 32 位程序突破 2G 内存限制 一般情况下&#xff0c;32 位程序的内存大小被限制在了 2G&#xff0c;不过可以通过以下的操作来突破这个限制。 修改操作系统参数 这一步骤只针对 32 位操作系统&#xff0c;64 位操作系统可以跳过 用管理员权限打开一个命令行窗口 执行…

【数据库原理及应用】——安全性与完整性(学习笔记)

&#x1f4d6; 前言&#xff1a;随着计算机的普及&#xff0c;数据库的使用也越来越广泛。为了适应和满足数据共享的环境和要求&#xff0c;DBMS要对数据库进行保护&#xff0c;保证整个系统的正常运转&#xff0c;防止数据意外丢失、被窃取和不一致数据产生&#xff0c;以及当…

【Linux】---认识进程

文章目录进程的概念1、什么是进程2、PCB3、查看进程4、初始fork()创建进程5、kill杀死进程进程的状态普适的操作系统下的状态运行阻塞挂起Linux下的状态理解R--运行状态S--睡眠状态D--深度睡眠状态T--暂停状态t--追踪暂停状态X--死亡状态Z--僵尸状态孤儿进程进程优先级进程的其…

先广度后深度,打开编程视野

古人云 “读万卷书&#xff0c;行万里路。” 书籍是人类进步的阶梯、培养阅读习惯&#xff0c;当一个人爱上读书的时候&#xff0c;眼睛都是发光的。 在小编看来&#xff0c;学习理念是【先广度后深度】&#xff0c;先把Java知识体系的东西都了解到&#xff0c;工作上先会用&…

【数据结构与算法】之动态规划经典问题

前言 本文为 【数据结构与算法】动态规划 经典问题相关介绍 &#xff0c;具体将对最长递增子序列问题&#xff0c;找零钱问题&#xff0c;0-1背包问题相关动态规划算法问题进行详尽介绍~ &#x1f4cc;博主主页&#xff1a;小新要变强 的主页 &#x1f449;Java全栈学习路线可…

【LeetCode】No.91. Decode Ways -- Java Version

题目链接&#xff1a;https://leetcode.com/problems/decode-ways/ 1. 题目介绍&#xff08;Decode Ways&#xff09; A message containing letters from A-Z can be encoded into numbers using the following mapping: 【Translate】&#xff1a; 包含从A到z的字母的消息可…

C++之继承详解(万字讲解)

这里是目录呀前言一、继承的概念及定义1.继承的概念2.继承定义(1)定义格式(2)继承关系和访问限定符(3)继承基类成员访问方式的变化二、继承中的作用域三、基类和派生类对象赋值转换四、派生类的默认成员函数五、继承与友元六、继承与静态成员七.复杂的菱形继承及菱形虚拟继承1.…