C语言-综合案例:通讯录

news2024/11/25 20:16:21

传送门:C语言-第九章-加餐:文件位置指示器与二进制读写

目录

第一节:思路整理

第二节:代码编写

        2-1.通讯录初始化

        2-2.功能选择

        2-3.增加 和 扩容

        2-4.查看

        2-5.查找

        2-6.删除

        2-7.修改

        2-8.退出

第三节:测试

下期预告:


第一节:思路整理

        创建三个文件,main.c用来测试程序,Function.c用来存放函数定义,Address.h用来存放函数声明、全局变量和各种宏。

        通讯录的需求是能够长久的存储信息,这就需要用到文件操作,每次运行通讯录程序时从文件读取数据,每次关闭程序时就把数据重新写入到文件中。

        存储数据的容器:

        可以定义一个结构体类型 PeopleInfo,每有一个人就用这个类型存储它的所有信息;再定义一个结构体类型 AddressBook,它用来存储所有 PeopleInfo,并且还有通讯录的容量和已存储数量,便于管理。

Address.h
struct PeopleInfo // 一个人的信息
{
	char _name[20];   // 名字
	int _age;         // 年龄
	char _number[20]; // 号码
};
struct AddressBook // 通讯录 
{
	struct PeopleInfo* _PeoPle; // 所有人的信息
	int _capacity; // 通讯录的容量
	int _size;     // 通讯录当前存储的数量
}addressBook;

        其他问题我们在写代码的过程中一一呈现。

第二节:代码编写

        我们把通讯录的各种功能一一实现:

        2-1.通讯录初始化

        通讯录初始化包括设定通讯录的初始容量、从文件获取信息:

Address.h
#define DEL_CAPACITY 10 // 默认初始容量 
Function.c
void AddressInit()
{
	FILE* pf = fopen(".//AddressBook.txt","rb"); // 二进制读方式打开
	if (pf == NULL)
	{
		perror("文件打开失败\n");
		return;
	}
	addressBook._capacity = DEL_CAPACITY;
	addressBook._PeoPle = (struct PeopleInfo*)malloc(addressBook._capacity*sizeof(struct PeopleInfo)); // 开辟空间
	if (addressBook._PeoPle == NULL)
		return;
	addressBook._size = 0;
	// 从文件中读取数据
	while (0 != fread(addressBook._PeoPle + addressBook._size, sizeof(struct PeopleInfo), 1, pf))
	{
		addressBook._size++;
		if (addressBook._size == addressBook._capacity)
			Expansion(); // 扩容函数,之后实现
	}
    fclose(pf);
}

        2-2.功能选择

        通讯录初始化完成后,就编写代码实现对各种功能的选择:

Function.c
void printMenu()
{
	printf("***********************************************\n");
	printf("***************1.增加*****2.删除***************\n");
	printf("***************3.查找*****4.修改***************\n");
	printf("***************5.查看*****0.退出***************\n");
	printf("***********************************************\n");
}

void Select()
{
	printMenu(); // 打印菜单
	int select;
	while (1)
	{
		printf("请选择:>");
		scanf("%d", &select);
		switch (select)
		{
		case 1:
			ADD(); // 增加
			break;
		case 2:
			DEL(); // 删除
			break;
		case 3:
			Find();// 查找
			break;
		case 4:
			MOD(); // 修改 
			break;
		case 5:
			DIS(); // 查看
			break;
		case 0:
			EXIT();// 退出
			return 0;
		default:
			printf("非法的请求,请重新输入:>");
		}
	}
}

        switch 中的函数在之后一一实现。

        2-3.增加 和 扩容

Function.c
void ADD()
{
	struct PeopleInfo person;
	printf("请输入姓名:>");
	scanf("%s", person._name);
	printf("请输入年龄:>");
	scanf("%d", &person._age);
	printf("请输入号码:>");
	scanf("%s", person._number);
	// 把个人信息添加到通讯录
	memmove(addressBook._PeoPle+addressBook._size,&person,sizeof(person));
	addressBook._size++;
	printf("添加成功!\n"); // 提示信息
	if (addressBook._size == addressBook._capacity)
		Expansion(); // 扩容
}

        如果满了就扩容:

Address.h
#define EXP_CAPACITY 5  // 每次扩容的大小
Function.c
void Expansion()
{
	// 使用realloc保留原来的内容
	struct PeoPle* tmp = 
    (struct PeoPle*)realloc(addressBook._PeoPle, addressBook._capacity + EXP_CAPACITY);
	if (tmp != NULL)
		addressBook._PeoPle = tmp;
	else
		printf("扩容失败!\n");
	// 更新容量
	addressBook._capacity = addressBook._capacity + EXP_CAPACITY;
	printf("扩容成功!当前容量:%d\n",addressBook._capacity); // 提示信息
}

        2-4.查看

        查看功能可以查看已经添加的联系人的所有信息:

Function.c
void DIS()
{
	printf("姓名			年龄			号码\n");
	for (int i = 0; i < addressBook._size; i++)
	{
		printf("%s			%d			%s\n",addressBook._PeoPle[i]._name, addressBook._PeoPle[i]._age, addressBook._PeoPle[i]._number);
	}
}

        2-5.查找

        查找是根据名字找到指向他信息的下标,找不到返回-1:

Function.c
int Find(const char* name)
{

	for (int i = 0; i < addressBook._size; i++)
	{
		if (strcmp(addressBook._PeoPle[i]._name, name) == 0)
			return i;
	}
	printf("该联系人不存在!\n");
	return -1;
}

        Select 函数中的case 3的部分做如下修改: 

case 3:
	printf("请输入要查找的联系人:>");
	char name[20]; 
	scanf("%s",name);
	int i = Find(name);// 查找
	// 打印他的信息
	printf("姓名			年龄			号码\n");
	printf("%s			%d			%s\n", addressBook._PeoPle[i]._name, addressBook._PeoPle[i]._age, addressBook._PeoPle[i]._number);
	break;

        2-6.删除

        删除是根据名字删除指定的联系人,所以可以复用查找函数:

Function.c
void DEL()
{
	printf("请输入要删除的联系人:>");
	char name[20];
	scanf("%s",name);
	int i = Find(name);
	if (i >= 0)
	{
		// i 位置之后先前覆盖一位,就可以删除它了
		for (; i < addressBook._size - 1; i++)
		{
			addressBook._PeoPle[i] = addressBook._PeoPle[i + 1];
		}
		addressBook._size--;
        printf("删除成功!\n");
	}
}

        2-7.修改

        修改也是先根据名字找到相应下标,然后再修改,所以也要复用查找函数:

Fuction.c
void MOD()
{
	printf("请输入要修改的联系人:>");
	char name[20];
	scanf("%s", name);
	int i = Find(name);
	if (i >= 0)
	{
		printf("请修改姓名:>");
		scanf("%s", addressBook._PeoPle[i]._name);
		printf("请修改年龄:>");
		scanf("%d", &(addressBook._PeoPle[i]._age));
		printf("请修改号码:>");
		scanf("%s", addressBook._PeoPle->_number);
        printf("修改成功!\n");
	}
}

        2-8.退出

        程序退出时需要把数据存储到文件中,然后把堆区开辟的空间释放掉:

Function.c
void EXIT()
{
	// 存储数据
	FILE* pf = fopen(".//AddressBook.txt","wb"); // 二进制写方式打开
	if (pf == NULL)
	{
		perror("文件打开失败\n");
		return;
	}
	for (int i = 0; i < addressBook._size; i++)
	{
		fwrite(addressBook._PeoPle+i,sizeof(struct PeopleInfo),1 , pf);
	}
	fclose(pf);
	// 释放堆区空间
	free(addressBook._PeoPle);
}

        至此代码已经编写完毕了,可以测试程序了。

第三节:测试

        先添加3个人的信息,最后输入0退出消息:

        打开文件看里面是否有内容:

        因为是二进制读写,所以看不懂;

        再次运行程序,看看文件中的信息能不能正确读取:

 

        最后测试删除、修改、查看就可以了,这里不再掩饰。

每个文件的完整代码如下:

main.c
#include "Address.h"
int main()
{
	AddressInit();     // 初始化
	Select();          // 打印菜单并选择
	return 0;
}

 

Address.h
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define DEL_CAPACITY 10 // 默认初始容量 
#define EXP_CAPACITY 5  // 每次扩容的大小
struct PeopleInfo // 一个人的信息
{
	char _name[20];   // 名字
	int _age;         // 年龄
	char _number[20]; // 号码
};
struct AddressBook // 通讯录 
{
	struct PeopleInfo* _PeoPle; // 所有人的信息
	int _capacity; // 通讯录的容量
	int _size;     // 通讯录当前存储的数量
}addressBook;

void printMenu();   // 打印菜单
void Select();      // 打印菜单并选择
void AddressInit(); // 初始化通讯录
void Expansion();   // 扩容
void ADD();         // 增加
void DIS();         // 查看
int Find(const char* name); // 查找
void DEL();         // 删除
void MOD();         // 修改
void EXIT();        // 保存+释放
Function.c
#include "Address.h"
void AddressInit()
{
	FILE* pf = fopen(".//AddressBook.txt","rb"); // 二进制读方式打开
	if (pf == NULL)
	{
		perror("文件打开失败\n");
		return;
	}
	addressBook._capacity = DEL_CAPACITY;
	addressBook._PeoPle = (struct PeopleInfo*)malloc(addressBook._capacity*sizeof(struct PeopleInfo)); // 开辟空间
	if (addressBook._PeoPle == NULL)
		return;
	addressBook._size = 0;
	// 从文件中读取数据
	while (0 != fread(addressBook._PeoPle + addressBook._size, sizeof(struct PeopleInfo), 1, pf))
	{
		addressBook._size++;
		if (addressBook._size == addressBook._capacity)
			Expansion(); // 扩容函数,之后实现
	}
	fclose(pf);
}

void printMenu()
{
	printf("***********************************************\n");
	printf("***************1.增加*****2.删除***************\n");
	printf("***************3.查找*****4.修改***************\n");
	printf("***************5.查看*****0.退出***************\n");
	printf("***********************************************\n");
}

void Select()
{
	printMenu(); // 打印菜单
	int select;
	while (1)
	{
		printf("请选择:>");
		scanf("%d", &select);
		switch (select)
		{
		case 1:
			ADD(); // 增加
			break;
		case 2:
			DEL(); // 删除
			break;
		case 3:
			printf("请输入要查找的联系人:>");
			char name[20]; 
			scanf("%s",name);
			int i = Find(name);// 查找
			// 打印他的信息
			printf("姓名			年龄			号码\n");
			printf("%s			%d			%s\n", addressBook._PeoPle[i]._name, addressBook._PeoPle[i]._age, addressBook._PeoPle[i]._number);
			break;
		case 4:
			MOD(); // 修改 
			break;
		case 5:
			DIS(); // 查看
			break;
		case 0:
			EXIT();// 退出
			return 0;
		default:
			printf("非法的请求,请重新输入:>");
		}
	}
}

void ADD()
{
	struct PeopleInfo person;
	printf("请输入姓名:>");
	scanf("%s", person._name);
	printf("请输入年龄:>");
	scanf("%d", &person._age);
	printf("请输入号码:>");
	scanf("%s", person._number);
	// 把个人信息添加到通讯录
	memmove(addressBook._PeoPle+addressBook._size,&person,sizeof(person));
	addressBook._size++;
	printf("添加成功!\n"); // 提示信息
	if (addressBook._size == addressBook._capacity)
		Expansion(); // 扩容
}

void Expansion()
{
	// 使用realloc保留原来的内容
	struct PeopleInfo* tmp = (struct PeopleInfo*)realloc(addressBook._PeoPle, addressBook._capacity + EXP_CAPACITY);
	if (tmp != NULL)
		addressBook._PeoPle = tmp;
	else
		printf("扩容失败!\n");
	// 更新容量
	addressBook._capacity = addressBook._capacity + EXP_CAPACITY;
	printf("扩容成功!当前容量:%d\n",addressBook._capacity); // 提示信息
}

void DIS()
{
	printf("姓名			年龄			号码\n");
	for (int i = 0; i < addressBook._size; i++)
	{
		printf("%s			%d			%s\n",addressBook._PeoPle[i]._name, addressBook._PeoPle[i]._age, addressBook._PeoPle[i]._number);
	}
}

int Find(const char* name)
{

	for (int i = 0; i < addressBook._size; i++)
	{
		if (strcmp(addressBook._PeoPle[i]._name, name) == 0)
			return i;
	}
	printf("该联系人不存在!\n");
	return -1;
}

void DEL()
{
	printf("请输入要删除的联系人:>");
	char name[20];
	scanf("%s",name);
	int i = Find(name);
	if (i >= 0)
	{
		// i 位置之后先前覆盖一位,就可以删除它了
		for (; i < addressBook._size - 1; i++)
		{
			addressBook._PeoPle[i] = addressBook._PeoPle[i + 1];
		}
		addressBook._size--;
		printf("删除成功!\n");
	}
}

void MOD()
{
	printf("请输入要修改的联系人:>");
	char name[20];
	scanf("%s", name);
	int i = Find(name);
	if (i >= 0)
	{
		printf("请修改姓名:>");
		scanf("%s", addressBook._PeoPle[i]._name);
		printf("请修改年龄:>");
		scanf("%d", &(addressBook._PeoPle[i]._age));
		printf("请修改号码:>");
		scanf("%s", addressBook._PeoPle->_number);
		printf("修改成功!\n");
	}
}

void EXIT()
{
	// 存储数据
	FILE* pf = fopen(".//AddressBook.txt","wb"); // 二进制写方式打开
	if (pf == NULL)
	{
		perror("文件打开失败\n");
		return;
	}
	for (int i = 0; i < addressBook._size; i++)
	{
		fwrite(addressBook._PeoPle+i,sizeof(struct PeopleInfo),1 , pf);
	}
	fclose(pf);
	// 释放堆区空间
	free(addressBook._PeoPle);
}

下期预告:

        下一次是第十章,将学习预编译相关知识

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

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

相关文章

基于SpringBoot+Vue的超市外卖管理系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、SSM项目源码 系统展示 【2025最新】基于JavaSpringBootVueMySQL的…

OceanBase 企业版OMS 4.2.3的使用

OceanBase 企业版OMS 4.2.3的使用 一、界面说明 1.1 概览 1.2 数据迁移 1.3 数据同步 1.4 数据源管理 1.5 运维监控 1.6 系统管理 二、功能说明 注意&#xff1a; 在数据迁移与数据同步的功能中&#xff0c;如果涉及到增量操作&#xff1a; 1.需要使用sys租户的用…

828华为云征文 | 华为云Flexusx与Docker技术融合,打造个性化WizNote服务

前言 华为云Flexusx携手Docker技术&#xff0c;创新融合打造高效个性化WizNote服务。Flexusx的柔性算力与Docker的容器化优势相结合&#xff0c;实现资源灵活配置与性能优化&#xff0c;助力企业轻松构建稳定、高效的云端笔记平台。828华为云企业上云节特惠来袭&#xff0c;Fle…

【无标题】Efinity 0基础进行流水灯项目撰写(FPGA)

文章目录 前言一、定义概念 缩写1. 二、性质1.2. 三、使用步骤编译常见错误1. 没加分号2. end 写多了 编译成功的标志总结参考文献 前言 数电课设 使用 FPGAIDE 使用 Efinity 一、定义概念 缩写 1. 二、性质 1. 2. 三、使用步骤 python代码块matlab代码块c代码块编译…

你真的了解Canvas吗--解密二【ZRender篇】

书接上文你真的了解Canvas吗--解密一【ZRender篇】 目录 入口 挖掘 继承 _init step-1&#xff1a;取所有key值 ​​​​​​​ step-2&#xff1a;定义构造函数BezierCurveShape …

PMP--一模--解题--1-10

文章目录 14.敏捷--方法--替代敏捷方法--看板1、 [单选] 根据项目的特点&#xff0c;项目经理建议选择一种敏捷方法&#xff0c;该方法限制团队成员在任何给定时间执行的任务数。此方法还允许团队提高工作过程中问题和瓶颈的可见性。项目经理建议采用以下哪种方法&#xff1f; …

金属铬厂商分析:前十强厂商占有大约64.0%的市场份额

金属铬是一种灰色、有光泽、硬而脆的过渡金属。铬是不锈钢的主要添加剂&#xff0c;可增加耐腐蚀性。 据QYResearch调研团队最新报告“全球金属铬市场报告2024-2030”显示&#xff0c;预计2030年全球金属铬市场规模将达到11.8亿美元&#xff0c;未来几年年复合增长率CAGR为6.5%…

【数据结构与算法】受限线性表 --- 栈

【数据结构与算法】受限线性表 — 栈 文章目录 【数据结构与算法】受限线性表 --- 栈前言一、栈的基本概念二、栈的顺序存储三、栈的分文件编写四、栈的链式存储五、栈的应用案例-就近分配六、 中缀表达式转后缀表达式以及基于后缀表达式运算总结 前言 本篇文章就栈的基本概念…

医院HISPACS存储备份 要求全周期保存

内蒙古赤峰医院 HIS PACS数据备份 &#xff0c;质量好可以满足15-30年存储的空间需求&#xff0c;用着放心

NIDS——suricata(三)

一、监控ICMP流量 1、ICMP流量特征 四大特征分别为&#xff1a;消息类型&#xff08;Type&#xff09;、代码&#xff08;Code&#xff09;、校验和&#xff08;Checksum&#xff09;、数据字段&#xff08;Data Field&#xff09;。这里我们使用 type消息类型。 ICMP 消息的类…

Cookie、Web Storage介绍

概述 Cookie、LocalStorage、SessionStorage、IndexDB这些作为浏览器的存储入口&#xff0c;也是经典的八股文了&#xff0c;本文再次冷饭热吃来介绍这些API&#xff0c;主要是因为在其他文章中看到了一些个人感觉有用的小知识点&#xff0c;所以在这记录一下&#xff0c;以便…

招加盟商视频怎么拍效果好?

一定没有人比我更适合分享这篇文章了&#xff0c;我自己曾经就是做宣传片的&#xff0c;而且还有一家酸奶品牌做全国招商。 我来分享下加盟招商视频怎么拍效果好&#xff1f; 一、定脚步 说个题外话&#xff0c;以前我做传媒公司的时候&#xff0c;很多客户找我做宣传片&…

Ubuntu系统Docker部署数据库管理工具DbGate并实现远程查询数据

文章目录 前言1. 安装Docker2. 使用Docker拉取DbGate镜像3. 创建并启动DbGate容器4. 本地连接测试5. 公网远程访问本地DbGate容器5.1 内网穿透工具安装5.2 创建远程连接公网地址5.3 使用固定公网地址远程访问 前言 本文主要介绍如何在Linux Ubuntu系统中使用Docker部署DbGate数…

Windows安装字体的几种方式

1.选中你要安装的字体&#xff0c;然后右键&#xff0c;点击“为所有用户安装”&#xff0c;这样字体就会安装在C:\Windows\Fonts&#xff1b;如果你点了“安装”&#xff0c;那么就会安装在C:\Users\你电脑用户的名字\AppData\Local\Microsoft\Windows\Fonts。 像电脑自带的字…

strncmp函数的使用

目录 1.头文件 2.strncmp函数的使用 小心&#xff01;VS2022不可直接接触&#xff0c;否则&#xff01;没这个必要&#xff0c;方源面色淡然一把抓住&#xff01;顷刻炼化&#xff01; 1.头文件 strncmp函数的使用需要头文件 #include<string.h> 2.strncmp函数的使用 …

第一篇:教你轻松部署本地大模型(Ollama)

一、前言 搞研发的&#xff0c;想学习大模型的&#xff0c;很多都想本地部署一波&#xff0c;体验一下&#xff0c;部署是学习的第一步&#xff0c;我们不仅仅是要理论的巨人&#xff0c;还要成为实战的专家。不要恐惧&#xff0c;不要恐惧&#xff0c;不要恐惧&#xff0c;重…

【数学建模】典型相关分析

典型相关分析&#xff08;Canonical Correlation Analysis, CCA&#xff09;是一种统计方法&#xff0c;用于寻找两个多变量数据集之间的线性关系。这种分析方法可以用来衡量两个数据集之间的相关性&#xff0c;并找出能够最好地解释这种相关性的变量组合。 典型相关分析的基本…

Android系列基础知识总结

四大组件 Activity Activity生命周期 不同场景下Activity生命周期的变化过程 启动Activity&#xff1a; onCreate()—>onStart()—>onResume()&#xff0c;Activity进入运行状态。Activity退居后台&#xff1a; 当前Activity转到新的Activity界面或按Home键回到主屏&a…

WebGL系列教程二(环境搭建及着色器初始化)

目录 1 前言2 新建html页面3 着色器介绍3.1 顶点着色器、片元着色器与光栅化的概念3.2 声明顶点着色器3.3 声明片元着色器 4 坐标系(右手系)介绍5 着色器初始化5.1 给一个画布canvas5.2 获取WebGL对象5.3 创建着色器对象5.4 获取着色器对象的源5.5 绑定着色器的源5.6 编译着色器…

好看好听的小猪包扩音器,轻巧便携更好用,得胜E10上手

我们以前在学校、景区等地方&#xff0c;都见过教师、讲解员所使用的扩音器&#xff0c;这类设备大多做得笨重粗糙&#xff0c;而且音质一般&#xff0c;声音特别粗糙刺耳&#xff0c;对使用者和旁观者影响都不好。最近我发现了一款得胜E10扩音器&#xff0c;大大提升了这种便携…