【C语言】通讯录3.0 (文件存储版)
前言
通讯录是一种记录联系人信息的工具,包括姓名、电话号码、电子邮件地址、住址等。
文章的一二三章均于上一篇相同,可以直接看第四章改造内容。
此通讯录是基于通讯录2.0(动态增长版)的基础上进行增加文件操作功能,请先看系列文章第二篇,再看本篇博客。
****** 有需要源代码,见文章末尾 ******
系列文章目录
第一篇:【C语言】通讯录1.0 (静态版)
第二篇:【C语言】通讯录2.0 (动态增长版)
第三篇:【C语言】通讯录3.0 (文件存储版)
文章目录
- 前言
- 系列文章目录
- 一、什么是通讯录
- 二、静态版、动态增长版和文件存储版的区别
- 1. 静态版
- 2. 动态增长版
- 3. 文件存储版
- 三、通讯录模块组成(图文)
- 1. 通讯录文件构成
- 2. 通讯录个人信息
- 3. 通讯录功能模块
- 四、改造通讯录2.0(改造目标)
- 1. 文件存储通讯录要求
- 2. 需要添加的功能模块
- 五、如何改进(代码演示)
- 1. 建立文件
- 2. 写入文件
- 3. 读取文件
- 六、所有文件代码
- 1.头文件
- 2. 函数文件
- 3. 逻辑测试文件
- 总结
一、什么是通讯录
通讯录是一种记录联系人信息的工具,包括姓名、电话号码、电子邮件地址、住址等。通讯录可以帮助人们管理自己的联系人,让人们更轻松地与他人保持联系。通讯录可以在手机、电脑、笔记本等设备上保存,也可以在云端储存和同步,方便用户随时查看和更新联系人信息。
二、静态版、动态增长版和文件存储版的区别
C语言静态版、动态增长版和文件存储版的区别如下:
1. 静态版
- 静态版:在程序编译时就确定了内存大小,程序运行期间内存大小不会发生变化,因此对于需要处理大量数据或者不确定数据大小的情况不适用。
2. 动态增长版
- 动态增长版:可以在程序运行期间根据需要动态增加内存大小,因此适用于处理不确定数据大小的情况。但是动态增长的内存需要手动释放,否则会导致内存泄漏。
3. 文件存储版
- 文件存储版:将数据存储在文件中,可以持久保存数据并随时读取。但是存储在文件中的数据需要进行IO操作,因此相比于内存操作来说效率较低。此外,文件存储版不适用于需要频繁修改的数据。
三、通讯录模块组成(图文)
1. 通讯录文件构成

2. 通讯录个人信息

3. 通讯录功能模块

四、改造通讯录2.0(改造目标)
1. 文件存储通讯录要求
- 将通讯录的联系人信息可以保存在文件里
- 保存到通讯录里的文件以二进制文件保存
- 第二次打开时,读取文件里上一次保存的联系人信息
2. 需要添加的功能模块

五、如何改进(代码演示)
1. 建立文件
- 使用文件操作
fopen函数来建立一个文件
FILE* pf = fopen("data.dat","wb");if (pf == NULL){perror("SaveContact");return;}
2. 写入文件
- 使用文件操作
fwrite函数 - 使用for循环,依次将通讯录内容写入文件
//写入文件
void SaveContact(Contact* pc)
{FILE* pf = fopen("data.dat","wb");if (pf == NULL){perror("SaveContact");return;}int i = 0;for (i = 0; i < pc->sz; i++){fwrite(pc->data+i, sizeof(PeoInfo), 1, pf);}fclose(pf);pf = NULL;printf("保存成功\n");
}
3. 读取文件
- 使用文件操作
fread函数 - 使用whlie循环,先判断
fread的返回值,为真进入循环 - 使用扩容函数
determine( )来判断内存是否充足,进行增容
//读取文件
void ReadContact(Contact* pc)
{FILE* pf = fopen("data.dat", "rb");if (pf == NULL){perror("ReadContact");return;}PeoInfo tem = { 0 };while (fread (&tem, sizeof(PeoInfo), 1, pf)){if (determine(pc) == 0){return;}pc->data[pc->sz] = tem;pc->sz++;}fclose(pf);pf = NULL;
}
六、所有文件代码
1.头文件
#pragma once
#include<stdio.h>
#include<assert.h>
#include<string.h>
#include<stdlib.h>#define MAX 100
#define NAME 10
#define SEX 5
#define TELE 12
#define ADDR 30
//使用枚举 定义选择
enum OPTION
{EXIT,//0ADD,DEL,SEARCH,MODIFY,SHOW,SORT
};//个人信息类型声明
typedef struct PeoInfo
{char name[NAME];int age;char sex[SEX];char tele[TELE];char addr[ADDR];
}PeoInfo;//建立通讯录
//静态版
//typedef struct Contact
//{
// PeoInfo data[MAX]; //通讯录数量
// int sz; //目前通讯录内的人数
//}Contact;
//动态版
typedef struct Contact
{PeoInfo* data;int sz;int capacity;}Contact;//函数声明//初始化通讯录
void InitContact(Contact* pc);//增加联系人
void AddContact(Contact* pc);//显示所有联系人的信息
void ShowContact(const Contact* pc);//删除指定联系人
void DelContact(Contact* pc);//查找指定联系人
void SearchContact(const Contact* pc);//修改指定联系人
void ModifyContact(Contact* pc);//释放内存void DestroyContact(Contact* pc);//保存通讯录
void SaveContact(Contact* pc);
2. 函数文件
#define _CRT_SECURE_NO_WARNINGS 1
#include "addbook.h"
void ReadContact(Contact* pc);
//静态版
//void InitContact(Contact* pc)
//{
// assert(pc); //断言
// memset(pc->data, 0, sizeof(pc->data)); //内存函数 data初始化为0
// pc->sz = 0;
//}
int determine(Contact* pc)
{assert(pc);if (pc->capacity == pc->sz){PeoInfo* ptr = (PeoInfo*)realloc(pc->data, (pc->capacity + 2) * sizeof(PeoInfo));if (ptr == NULL){perror("determine");return 0;}else{pc->capacity += 2;pc->data = ptr;printf("增容成功\n");return 1;}}return 1;
}
//动态版
void InitContact(Contact* pc)
{assert(pc);pc->data = (PeoInfo*)malloc(3 * sizeof(PeoInfo));if (pc->data == NULL){perror("InitContact");return;}pc->capacity = 3;pc->sz = 0;ReadContact(pc);
}
//动态版
void AddContact(Contact* pc)
{assert(pc); //断言if (determine(pc) == 0){return;}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++; //通讯录加1printf("联系人增加成功\n");
}//静态版
//void AddContact(Contact* pc)
//{
// assert(pc); //断言
// if (pc->sz == MAX) //如果通讯录已经满了 则返回
// {
// printf("通讯录已满,无法添加\n");
// return;
// }
// 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++; //通讯录加1
// printf("联系人增加成功\n");
//}
//搜索名字找通讯录函数
static int Findname(const Contact* pc, char na[])
{int i = 0;assert(pc && na);for (i = 0; i < pc->sz; i++){if (strcmp(pc->data[i].name, na) == 0){return i;}}return -1;
}
void DelContact(Contact* pc)
{if (pc->sz == 0){printf("通讯录为空\n");return;}char name[NAME] = { 0 };assert(pc);//输入要查找的联系人名字printf("请输入要查找的名字:>");scanf("%s", &name);//找到要查找的联系人int del = Findname(pc, name);//删除坐标位子的联系人 ,将后面的联系人进行代替其位置if (del == -1){printf("找不到,此人不存在\n");return;}else{int i = 0;for (i = del; i < pc->sz; i++){pc->data[i] = pc->data[i + 1];}pc->sz--;}printf("成功删除联系人\n");
}
void ShowContact(const Contact* pc)
{assert(pc);printf("%-10s\t%-4s\t%-5s\t%-12s\t%-30s\n", "名字", "年龄", "性别", "电话", "地址");int i = 0;for (i = 0; i < pc->sz; i++){printf("%-10s\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);}printf("通讯录展示完毕\n");
}
void SearchContact(const Contact* pc)
{if (pc->sz == 0){printf("通讯录为空\n");return;}char name[NAME] = { 0 };assert(pc);//输入要查找的联系人名字printf("请输入要查找的名字:>");scanf("%s", &name);//找到要查找的联系人int i = Findname(pc, name);if (i == -1){printf("找不到,此人不存在\n");return;}else{printf("%-10s\t%-4s\t%-5s\t%-12s\t%-30s\n", "名字", "年龄", "性别", "电话", "地址");printf("%-10s\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);}printf("成功找到联系人\n");
}
void ModifyContact(Contact* pc)
{assert(pc);char name[NAME] = { 0 };printf("请输入要修改人的名字:>");scanf("%s", &name);int mod = Findname(pc, name);if (mod == -1){printf("找不到,不存在\n");return;}else{printf("请输入姓名:>");scanf("%s", pc->data[mod].name);printf("请输入年龄:>");scanf("%d", &(pc->data[mod].age));printf("请输入性别:>");scanf("%s", pc->data[mod].sex);printf("请输入电话:>");scanf("%s", pc->data[mod].tele);printf("请输入地址:>");scanf("%s", pc->data[mod].addr);printf("联系人修改成功\n");}
}
void DestroyContact(Contact* pc)
{free(pc->data);pc->data = NULL;pc->capacity = 0;pc->sz;
}//写入文件
void SaveContact(Contact* pc)
{FILE* pf = fopen("data.dat","wb");if (pf == NULL){perror("SaveContact");return;}int i = 0;for (i = 0; i < pc->sz; i++){fwrite(pc->data+i, sizeof(PeoInfo), 1, pf);}fclose(pf);pf = NULL;printf("保存成功\n");
}
//读取文件
void ReadContact(Contact* pc)
{FILE* pf = fopen("data.dat", "rb");if (pf == NULL){perror("ReadContact");return;}PeoInfo tem = { 0 };while (fread (&tem, sizeof(PeoInfo), 1, pf)){if (determine(pc) == 0){return;}pc->data[pc->sz] = tem;pc->sz++;}fclose(pf);pf = NULL;
}
3. 逻辑测试文件
#define _CRT_SECURE_NO_WARNINGS 1
#include"addbook.h"
void menu()
{printf("********************************\n");printf("***** 1. ADD 2. DEL *****\n");printf("***** 3. SEARCH 4. MODIFY *****\n");printf("***** 5. SHOW 6. SORT *****\n");printf("***** 0. EXIT *****\n");printf("********************************\n");}void test()
{int input = 0;Contact con;InitContact(&con);do{menu();printf("请选择:>");scanf("%d", &input);switch (input){case ADD:AddContact(&con);break;case DEL:DelContact(&con);break;case SEARCH:SearchContact(&con);break;case MODIFY:ModifyContact(&con);break;case SHOW:ShowContact(&con);break;case SORT:printf("功能待开发\n");break;case EXIT:SaveContact(&con);DestroyContact(&con);printf("成功退出通讯录\n");break;default:printf("输入错误,请重新输入\n");break;}} while (input);}
int main()
{test();return 0;
}
总结
本期博客,是通讯录3.0(文件存储版),是对前面所学知识进行复习,编写通讯录时有助于理解自定义类型、动态内存管理及文件操作的学习和了解,通讯录的文章到这里就结束了,谢谢支持!!!
如这篇博客对大家有帮助的话,希望 三连 支持一下 !!! 如果有错误感谢大佬的斧正 如有 其他见解发到评论区,一起学习 一起进步。
相关文章:
【C语言】通讯录3.0 (文件存储版)
前言 通讯录是一种记录联系人信息的工具,包括姓名、电话号码、电子邮件地址、住址等。 文章的一二三章均于上一篇相同,可以直接看第四章改造内容。 此通讯录是基于通讯录2.0(动态增长版)的基础上进行增加文件操作功能,…...
【C#常用操作】
excel相关操作 using Excel Microsoft.Office.Interop.Excel;public Excel.Application app; public Excel.Workbooks wbs; public Excel.Workbook wb; public Excel.Worksheets wss; public Excel.Worksheet ws;/// <summary> /// 取得打开excel句柄 /// </summary…...
深入理解CountDownLatch计数器
入理解CountDownLatch计数器 其他知识点 Java 多线程基础 深入理解aqs ReentrantLock用法详解 深入理解信号量Semaphore 深入理解并发三大特性 并发编程之深入理解CAS 深入理解CountDownLatch Java 线程池 使用用法 CountDownLatch用法详解 CountDownLatch实现原理 下面例子来…...
从SQL注入绕过最新安全狗WAF中学习fuzz
前言 SQL注入并不是很精通,通过实战绕过WAF来进行加强SQL注入能力,希望对正在学习的师傅能有一丝帮助。 安装 安装前言 我是本地搭建的环境进行测试的 环境是windows11phpstudy2018sqli-labs phpstudy的安装我不再复述,这里简单说一下安全…...
C语言每日一题:12《数据结构》相交链表。
题目: 题目链接 思路一: 1.如果最后一个节点相同说明一定有交点。 2.使用两个循环获取一下长度,同时可以获取到尾节点。 3。注意初始化lenA和lenB为1,判断下一个节点是空是可以保留尾节点的。长度会少一个,尾节点没有…...
【Spring框架】SpringMVC
目录 什么是Spring MVC实现客户端和程序之间的“连接”1.1 RequestMapping1.2GetMapping1.3PostMapping 获取参数2.1.1 获取单个参数2.1.2 获取多个参数2.1.3 获取对象2.2 后端参数重命名2.3 RequestBody 接收JSON对象2.4 获取URL中参数PathVariable2.5 上传⽂件RequestPart2.6…...
HDFS中namenode安全模式
HDFS中namenode安全模式 安全模式的现象探究step1step2step3step4 安全模式的概述控制进入时间和离开条件安全模式自动进入离开安全模式手动进入离开 安全模式的现象探究 step1 HDFS集群在停机状态下,使用hdfs -daemon命令逐个进程启动集群,观察现象首…...
blender凹凸感和置换形变
一、怎么做出凹凸感 需要三个部分的内容: 1、一个基础的纹理:告诉计算机需要用一个什么样的纹理做凹凸,纹理一般采用黑白,在计算机里面,从 0 - 1之间的值可以用从黑到白之间不同的灰度来表示因此,有一张黑白…...
力扣 343. 整数拆分
题目来源:https://leetcode.cn/problems/integer-break/description/ C题解1:动态规划。dp[i] 代表数字i拆分后得到的最大乘积。递归公式为拆分后两个数的最大乘积相乘,即 dp[i] max(dp[i], dp[j] * dp[i-j])。对于n2或3需要另外讨论。 cla…...
【JavaWeb】正则表达式
🎄欢迎来到边境矢梦的csdn博文,本文主要讲解Java 中正则表达式 的相关知识🎄 🌈我是边境矢梦,一个正在为秋招和算法竞赛做准备的学生🌈 🎆喜欢的朋友可以关注一下🫰🫰&am…...
Vue中常用到的标签和指令
一、标签 在 Vue 中,并没有特定的标签是属于 Vue 的,因为 Vue 是一个用于构建用户界面的框架,可以与 HTML 标签一起使用。Vue 中可以使用的标签和元素基本上与 HTML 标准一致。 以下是一些常见的HTML标签,也可以在 Vue 中使用&a…...
C++设计模式之访问者模式
C访问者设计模式 文章目录 C访问者设计模式什么是设计模式什么是访问者设计模式该模式有什么优缺点优点缺点 如何使用 什么是设计模式 设计模式是一种通用的解决方案,用于解决特定的一类问题。它是一种经过验证的代码组织方式,可以帮助开发人员更快地实…...
Java8的stream常用的操作
记录一下常用的用法 定义测试对象 Datapublic class Employee {//idprivate Integer id;//姓名private String name;//年龄private Integer age;//身高private Double height;//存款private BigDecimal deposit;public Employee(Integer id, String name, Integer age, Double…...
传统计算机视觉
传统计算机视觉 计算机视觉难点图像分割基于主动轮廓的图像分割基于水平集的图像分割交互式图像分割基于模型的运动分割 目标跟踪基于光流的点目标跟踪基于均值漂移的块目标跟踪基于粒子滤波的目标跟踪基于核相关滤波的目标跟踪 目标检测一般目标检测识别之特征一般目标检测识别…...
13-3_Qt 5.9 C++开发指南_基于QReadWriteLock 的线程同步
使用互斥量时存在一个问题: 每次只能有一个线程获得互斥量的权限。如果在一个程序中有多个线程读取某个变量,使用互斥量时也必须排队。而实际上若只是读取一个变量,是可以让多个线程同时访问的,这样互斥量就会降低程序的性能。 例如…...
opencv04-掩膜
opencv04-掩膜 抠图 #include <iostream> #include <opencv2/highgui/highgui.hpp> #include <opencv2/opencv.hpp> #include <vector> #include <array> #include <algorithm>using namespace std; using namespace cv;int main() {str…...
python解析帆软cpt及frm文件(xml)获取源数据表及下游依赖表
#!/user/bin/evn python import os,re,openpyxl 输入:帆软脚本文件路径输出:帆软文件检查结果Excel#获取来源表 def table_scan(sql_str):# remove the /* */ commentsq re.sub(r"/\*[^*]*\*(?:[^*/][^*]*\*)*/", "", sql_str)# r…...
TypeScript
TypeScript 简称: TS ,是 JavaScript 的超集 ,简单来说就是: JS 有的 TS 都有 TypeScript Type JavaScript (在 JS 基础之上, 为 JS 添加了类型支持 ) TypeScript 是 微软 开发…...
解决启动vue前端报错:npm ERR! Missing script: “serve“
目录 一、遇到问题 二、出现报错的两个原因 三、解决办法 一、遇到问题 npm ERR! Missing script: "serve" npm ERR! npm ERR! To see a list of scripts, run: npm ERR! npm run npm ERR! A complet...
数据结构 | 线性数据结构——列表
目录 一、无序列表抽象数据类型 二、实现无序列表:链表 2.1 Node类 2.2 UnorderedList类 三、有序列表抽象数据类型 四、实现有序列表 列表是元素的集合,其中每一个元素都有一个相对于其他元素的位置。更具体地说,这种列表成为无序列表…...
[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解
突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 安全措施依赖问题 GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...
C++初阶-list的底层
目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...
TDengine 快速体验(Docker 镜像方式)
简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能,本节首先介绍如何通过 Docker 快速体验 TDengine,然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker,请使用 安装包的方式快…...
(十)学生端搭建
本次旨在将之前的已完成的部分功能进行拼装到学生端,同时完善学生端的构建。本次工作主要包括: 1.学生端整体界面布局 2.模拟考场与部分个人画像流程的串联 3.整体学生端逻辑 一、学生端 在主界面可以选择自己的用户角色 选择学生则进入学生登录界面…...
【C语言练习】080. 使用C语言实现简单的数据库操作
080. 使用C语言实现简单的数据库操作 080. 使用C语言实现简单的数据库操作使用原生APIODBC接口第三方库ORM框架文件模拟1. 安装SQLite2. 示例代码:使用SQLite创建数据库、表和插入数据3. 编译和运行4. 示例运行输出:5. 注意事项6. 总结080. 使用C语言实现简单的数据库操作 在…...
用docker来安装部署freeswitch记录
今天刚才测试一个callcenter的项目,所以尝试安装freeswitch 1、使用轩辕镜像 - 中国开发者首选的专业 Docker 镜像加速服务平台 编辑下面/etc/docker/daemon.json文件为 {"registry-mirrors": ["https://docker.xuanyuan.me"] }同时可以进入轩…...
【开发技术】.Net使用FFmpeg视频特定帧上绘制内容
目录 一、目的 二、解决方案 2.1 什么是FFmpeg 2.2 FFmpeg主要功能 2.3 使用Xabe.FFmpeg调用FFmpeg功能 2.4 使用 FFmpeg 的 drawbox 滤镜来绘制 ROI 三、总结 一、目的 当前市场上有很多目标检测智能识别的相关算法,当前调用一个医疗行业的AI识别算法后返回…...
AI书签管理工具开发全记录(十九):嵌入资源处理
1.前言 📝 在上一篇文章中,我们完成了书签的导入导出功能。本篇文章我们研究如何处理嵌入资源,方便后续将资源打包到一个可执行文件中。 2.embed介绍 🎯 Go 1.16 引入了革命性的 embed 包,彻底改变了静态资源管理的…...
使用 SymPy 进行向量和矩阵的高级操作
在科学计算和工程领域,向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能,能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作,并通过具体…...
USB Over IP专用硬件的5个特点
USB over IP技术通过将USB协议数据封装在标准TCP/IP网络数据包中,从根本上改变了USB连接。这允许客户端通过局域网或广域网远程访问和控制物理连接到服务器的USB设备(如专用硬件设备),从而消除了直接物理连接的需要。USB over IP的…...
