C语言-综合案例:通讯录
传送门: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;elseprintf("扩容失败!\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;elseprintf("扩容失败!\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);
}
下期预告:
下一次是第十章,将学习预编译相关知识
相关文章:
C语言-综合案例:通讯录
传送门:C语言-第九章-加餐:文件位置指示器与二进制读写 目录 第一节:思路整理 第二节:代码编写 2-1.通讯录初始化 2-2.功能选择 2-3.增加 和 扩容 2-4.查看 2-5.查找 2-6.删除 2-7.修改 2-8.退出 第三节:测试 下期…...
XWiki中添加 html 二次编辑失效
如果直接在 XWiki 中添加 html, 例如 修改颜色, 新窗口打开主页面等功能, 首次保存是生效的. 如果再次编辑, 则失效, 原因是被转换成了 Markdown 的代码, 而 Markdown 不支持. 解决这个问题可以使用 HTML 宏. 在 XWiki 中使用 Markdown 1.2 语法时,默认 Markdown …...
外贸|基于Java+vue的智慧外贸平台系统(源码+数据库+文档)
外贸|智慧外贸平台|外贸服务系统 目录 基于Javavue的智慧外贸平台系统 一、前言 二、系统设计 三、系统功能设计 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取: 博主介绍:✌️大厂码农|毕设布道师&…...
Elasticsearch:无状态世界中的数据安全
作者:来自 Elastic Henning Andersen 在最近的博客文章中,我们宣布了支持 Elastic Cloud Serverless 产品的无状态架构。通过将持久性保证和复制卸载到对象存储(例如 Amazon S3),我们获得了许多优势和简化。 从历史上…...
动手学习RAG:迟交互模型colbert微调实践 bge-m3
动手学习RAG: 向量模型动手学习RAG: BGE向量模型微调实践]()动手学习RAG: BCEmbedding 向量模型 微调实践]()BCE ranking 微调实践]()GTE向量与排序模型 微调实践]()模型微调中的模型序列长度]()相似度与温度系数 本文我们来进行ColBERT模型的实践,按惯例ÿ…...
springboot 整合quartz定时任务
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、pom的配置1.加注解 二、使用方法1.工程图2.创建工具类 三、controller 实现 前言 提示:这里可以添加本文要记录的大概内容: 提示&a…...
erlang学习: Mnesia Erlang数据库3
Mnesia数据库删除实现和事务处理 -module(test_mnesia). -include_lib("stdlib/include/qlc.hrl").-record(shop, {item, quantity, cost}). %% API -export([insert/3, select/0, select/1, delete/1, transaction/1,start/0, do_this_once/0]). start() ->mnes…...
善于善行——贵金属回收
在当今社会,贵金属回收已成为一项日益重要的产业。随 着科技的不断进步和人们对资源可持续利用的认识逐渐提高,贵金属回收的现状也备受关注。 目前,贵金属回收市场呈现出蓬勃发展的态势。一方面,贵金属如金、银、铂、钯等在众多领…...
用CSS 方式设置 table 样式
在现代Web开发中,使用CSS来设置table的样式是一种常见且强大的方法,它能让你的表格数据既美观又易于阅读。下面我将通过一个示例来展示如何使用现代CSS技巧来美化表格。 效果图 HTML 结构 首先,我们定义一个基本的HTML表格结构:…...
Elasticsearch7.x 集群迁移文档
一、集群样例信息 集群名称:escluster-ali-test 1、源集群:(source_cluster) 节点IP节点名称节点角色是否为master节点10.200.112.149es2.gj1.china-job.cndata,master是10.200.112.151es1.gj1.china-job.cndata,master否10.200.112.153es…...
高空抛物检测算法的应用场景解析
高空抛物事件频发,对公众安全构成严重威胁。无论是居民区还是商业中心,从高层建筑中丢弃物品都可能导致人员伤亡和财产损失。传统的监控手段多以事后追溯为主,无法在事发时及时预警和干预。为应对这一难题,视觉分析技术的发展为高…...
Leetcode 无重复字符的最长子串
算法思想: 滑动窗口:通过 start 和 end 来维护一个滑动窗口,start 指向当前窗口的起点,end 是当前窗口的末尾。滑动窗口中的字符都是无重复的。哈希表 charIndexMap:用于存储每个字符及其最近一次出现的位置。更新起始…...
用命令行的方式启动.netcore webapi
用命令行的方式启动.netcore web项目 进入指定的项目文件夹,比如我发布后的代码放在下面文件夹中 在此地址栏中输入“cmd”,打开命令提示符,进入到发布代码目录 命令行启动.netcore项目的命令为: dotnet 项目启动文件.dll --urls"ht…...
Spring6详细学习笔记(IOC+AOP)
一、Spring系统架构介绍 1.1、定义 Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器(框架)。Spring官网 Spring是一款主流的Java EE 轻量级开源框架,目的是用于简化Java企业级引用的开发难度和开发周期。从简单性、可测试性和松耦…...
@RequestMapping 基于哪个库进行通信
RequestMapping 是 Spring Framework 中用于处理 HTTP 请求的注解,主要用于定义控制器方法的请求映射。它并不直接基于某个特定的通信库,而是依赖于 Spring MVC 框架的核心功能。 1. Spring MVC RequestMapping 是 Spring MVC 的一部分,Spr…...
GPIO(General Purpose Input/Output)输入/输出
GPIO最简单的功能是输出高低电平;GPIO还可以被设置为输入功能,用于读取按键等输入信号;也可以将GPIO复用成芯片上的其他外设的控制引脚。 STM32F407ZGT6有8组IO。分别为GPIOA~GPIOH,除了GPIOH只有两个IO,其余每组IO有…...
两个pdf合并成一个pdf,这些pdf合并小技巧了解下
在日常工作和学习中,我们经常会遇到需要将多个PDF文件合并成一个文件的情况。这不仅可以提高文件管理的效率,还能让信息展示更加集中和便捷。今天就来给大家分享几种非常简单便捷的PDF合并小技巧,一起来学习下吧。 方法一:WPS WP…...
Transformer学习(2):自注意力机制
回顾 注意力机制 自注意力机制 自注意力机制中同样包含QKV,但它们是同源(Q≈K≈V),也就是来自相同的输入数据X,X可以分为 ( x 1 , x 2 , . . , x n ) (x_1,x_2,..,x_n) (x1,x2,..,xn)。 而通过输入嵌入层(input embedding),…...
分类预测|基于粒子群优化径向基神经网络的数据分类预测Matlab程序PSO-RBF 多特征输入多类别输出 含基础RBF程序
分类预测|基于粒子群优化径向基神经网络的数据分类预测Matlab程序PSO-RBF 多特征输入多类别输出 含基础RBF程序 文章目录 一、基本原理1. 粒子群优化算法(PSO)2. 径向基神经网络(RBF)PSO-RBF模型流程总结 二、实验结果三、核心代码…...
【React】Vite 构建 React
项目搭建 vite 官网:Vite 跟着文档走即可,选择 react ,然后 ts swc。 着重说一下 package-lock.json 这个文件有两个作用: 锁版本号(保证项目在不同人手里安装的依赖都是相同的,解决版本冲突的问题&am…...
深度学习在微纳光子学中的应用
深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向: 逆向设计 通过神经网络快速预测微纳结构的光学响应,替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…...
java_网络服务相关_gateway_nacos_feign区别联系
1. spring-cloud-starter-gateway 作用:作为微服务架构的网关,统一入口,处理所有外部请求。 核心能力: 路由转发(基于路径、服务名等)过滤器(鉴权、限流、日志、Header 处理)支持负…...
通过Wrangler CLI在worker中创建数据库和表
官方使用文档:Getting started Cloudflare D1 docs 创建数据库 在命令行中执行完成之后,会在本地和远程创建数据库: npx wranglerlatest d1 create prod-d1-tutorial 在cf中就可以看到数据库: 现在,您的Cloudfla…...
3.3.1_1 检错编码(奇偶校验码)
从这节课开始,我们会探讨数据链路层的差错控制功能,差错控制功能的主要目标是要发现并且解决一个帧内部的位错误,我们需要使用特殊的编码技术去发现帧内部的位错误,当我们发现位错误之后,通常来说有两种解决方案。第一…...
Java如何权衡是使用无序的数组还是有序的数组
在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...
安宝特方案丨XRSOP人员作业标准化管理平台:AR智慧点检验收套件
在选煤厂、化工厂、钢铁厂等过程生产型企业,其生产设备的运行效率和非计划停机对工业制造效益有较大影响。 随着企业自动化和智能化建设的推进,需提前预防假检、错检、漏检,推动智慧生产运维系统数据的流动和现场赋能应用。同时,…...
屋顶变身“发电站” ,中天合创屋面分布式光伏发电项目顺利并网!
5月28日,中天合创屋面分布式光伏发电项目顺利并网发电,该项目位于内蒙古自治区鄂尔多斯市乌审旗,项目利用中天合创聚乙烯、聚丙烯仓库屋面作为场地建设光伏电站,总装机容量为9.96MWp。 项目投运后,每年可节约标煤3670…...
是否存在路径(FIFOBB算法)
题目描述 一个具有 n 个顶点e条边的无向图,该图顶点的编号依次为0到n-1且不存在顶点与自身相连的边。请使用FIFOBB算法编写程序,确定是否存在从顶点 source到顶点 destination的路径。 输入 第一行两个整数,分别表示n 和 e 的值(1…...
docker 部署发现spring.profiles.active 问题
报错: org.springframework.boot.context.config.InvalidConfigDataPropertyException: Property spring.profiles.active imported from location class path resource [application-test.yml] is invalid in a profile specific resource [origin: class path re…...
PAN/FPN
import torch import torch.nn as nn import torch.nn.functional as F import mathclass LowResQueryHighResKVAttention(nn.Module):"""方案 1: 低分辨率特征 (Query) 查询高分辨率特征 (Key, Value).输出分辨率与低分辨率输入相同。"""def __…...
