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);
}

下期预告:

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

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

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

相关文章

【单片机开发】单片机常用开发工具

【前言】 在嵌入式系统领域&#xff0c;单片机&#xff08;Microcontroller, MCU&#xff09;作为核心组件&#xff0c;广泛应用于智能家居、工业控制、汽车电子等众多领域。而单片机开发工具&#xff0c;则是开发者们实现创意、解决问题的重要助手。本文主要讲述目前主流的单…

港科夜闻 | 叶玉如校长出席2024科技+新质生产力高峰论坛发表专题演讲,贡献国家科技强国战略...

关注并星标 每周阅读港科夜闻 建立新视野 开启新思维 1、叶玉如校长出席“2024科技新质生产力高峰论坛”&#xff0c;做了题为“三个创新&#xff1a;培育和发展新质生产力、贡献国家科技强国战略”的主题演讲。该论坛于9月2日在香港召开。论坛围绕夯实基础科研、推动源头创新、…

axure判断

在auxre中我们也可以实现判断的功能&#xff0c;当目标等于什么内容时则执行下方的功能。 一、判断输入框中是否有值 画布添加一个输入框、一个文本标签删除其中内容&#xff0c;添加一个按钮&#xff0c;输入框命名为【文本显示】文本标签命名为【提示】 给按钮新增一个交互…

单向链表概述

文章目录 &#x1f34a;自我介绍&#x1f34a;单向链表概述数据域和指针域数据类型设计 你的点赞评论就是对博主最大的鼓励 当然喜欢的小伙伴可以&#xff1a;点赞关注评论收藏&#xff08;一键四连&#xff09;哦~ &#x1f34a;自我介绍 Hello,大家好&#xff0c;我是小珑也要…

(计算机网络)应用层

1.为什么需要应用层 应用层提供使用tcp&#xff0c;udp使用的方式 协议就是制定的规则 2.域名服务器概述 域名是唯一的 新增域名&#xff0c;大家都要修改这个文本文件&#xff0c;所以要进行集中管理这个文本文件&#xff0c;而不是使用本地的hosts文件 hosts文件在Windows系统…

python 图片识别文字

要在 Python 中实现图片中的文字识别&#xff0c;通常使用的是 Tesseract OCR&#xff0c;结合 Pillow 处理图像。以下是一个简易的实现步骤&#xff1a; 1. 安装所需库&#xff1a; 你需要安装以下库&#xff1a; Tesseract OCR 引擎&#xff1a;这是进行文字识别的核心工具…

循环语句(C语言)

一般情况下&#xff0c;语句是按顺序执行的&#xff1a;函数中的第一条语句先执行&#xff0c;接着是第二条语句&#xff0c;依此类推。循环语句允许我们多次执行一个语句或语句组。 “ while ”循环 功能描述&#xff1a;当给定条件为真时&#xff0c;重复语句或语句组。它会…

内核线程同步之信号量、互斥量、自旋锁、原子量

本次主要是学习线程同步时保护数据的锁机制,对于多个线程同时对一个变量进行赋值操作时,会造成该变量的赋值不确定,出现了竞争状态,这时需要有个锁来保护下,即加个锁,同一时间只能有一个线程对其操作,当锁释放了,另一线程才能对其操作,而处在加锁和解锁之间的区域叫做…

Java | Leetcode Java题解之第397题整数替换

题目&#xff1a; 题解&#xff1a; class Solution {public int integerReplacement(int n) {int ans 0;while (n ! 1) {if (n % 2 0) {ans;n / 2;} else if (n % 4 1) {ans 2;n / 2;} else {if (n 3) {ans 2;n 1;} else {ans 2;n n / 2 1;}}}return ans;} }

部署定时任务每2天清理一次表

1、创建存储过程 create or replace procedure truct authid current_user ---使用“authid Current_user”将存储过程转化为调用者权限 as begin execute immediate truncate table rep.tmp_s_st_busi_send_arc; end; / 2、创建定时任务 begin dbms_scheduler.create…

机器学习中的聚类艺术:探索数据的隐秘之美

一 什么是聚类 聚类是一种经典的无监督学习方法&#xff0c;无监督学习的目标是通过对无标记训练样本的学习&#xff0c;发掘和揭示数据集本身潜在的结构与规律&#xff0c;即不依赖于训练数据集的类标记信息。聚类则是试图将数据集的样本划分为若干个互不相交的类簇&#xff…

SEELE 框架是

SEELE 框架是一个相对新颖的组织管理和优化框架&#xff0c;旨在帮助团队或企业更好地实现目标。它的核心思想是通过科学的管理方法来提升组织的执行力和决策能力。以下是对 SEELE 框架的详细讲解&#xff0c;包括定义、内容、实施步骤、实施策略以及推荐的实践方法和工具。 一…

【人工智能学习笔记】4_3 深度学习基础之循环神经网络

循环神经网络(Recurrent Neural Network, RNN) 是一类以序列(sequence)数据为输入,在序列的演进方向进行递归(recursion)且所有节点(循环单元)按链式连接的递归神经网络(recursive neural network),循环神经网络具有短期记忆能力 RNN核心思想 RNN的结构 一个典型…

【JS逆向学习】快乐学堂登陆接口(自定义DES加密、ddddocr验证码识别)

逆向目标 网址&#xff1a;https://www.91118.com/Passport/Account/Login接口&#xff1a;https://www.91118.com/passport/Account/LoginPost参数&#xff1a; passr 逆向过程 输入手机号、密码、验证码 点击登陆&#xff0c;多试几次&#xff0c;然后观察并比较不通请求…

K8s之DNS方案

在使用k8s过程中&#xff0c;有时需要配置自定义的host文件做主机名解析。如果在镜像中修改/etc/hosts文件&#xff0c;而容器启动初始化时&#xff0c;会覆盖掉该文件。就需要利用k8s自身提供的机制解决类似的问题。具体如下&#xff1a; 1. hostAliases hostAliases 是 Kub…

鸿蒙界面开发——组件(7):组件导航 页面路由

组件导航 (Navigation)(推荐) Navigation() Navigation(pathInfos: NavPathStack)Navigation是路由容器组件&#xff0c;一般作为首页的根容器&#xff0c;包括单栏(Stack)、分栏(Split)和自适应(Auto)三种显示模式。Navigation组件适用于模块内和跨模块的路由切换&#xff0c…

ApacheKafka中的设计

文章目录 1、介绍1_Kafka&MQ场景2_Kafka 架构剖析3_分区&日志4_生产者&消费者组5_核心概念总结6_顺写&mmap7_Kafka的数据存储形式 2、Kafka的数据同步机制1_高水位&#xff08;High Watermark&#xff09;2_LEO3_高水位更新机制4_副本同步机制解析5_消息丢失问…

网络编程9.10

使用数据库完成工人管理系统: ubuntuubuntu:DB$ ubuntuubuntu:DB$ cat 2.c #include <myhead.h> #include <sqlite3.h> #include <string.h>typedef struct {int id;char name[20];double salary; } Worker;int do_insert(sqlite3 *ppDb) {Worker work;pri…

数学基础 -- 勒让德多项式之矩阵与内积

勒让德多项式与内积计算 1. 欧几里得空间中的向量内积 在欧几里得空间中&#xff0c;向量的内积定义为&#xff1a; ⟨ v , w ⟩ x 1 y 1 x 2 y 2 ⋯ x n y n \langle \mathbf{v}, \mathbf{w} \rangle x_1 y_1 x_2 y_2 \cdots x_n y_n ⟨v,w⟩x1​y1​x2​y2​⋯xn​…

数据结构应用实例(三)——赫夫曼编码

Content&#xff1a; 一、问题描述二、算法思想三、代码实现四、小结 一、问题描述 对一篇英文文章&#xff0c;统计各字符&#xff08;仅限于26个小写字母&#xff09;出现的次数&#xff0c;并据此进行 Huffman 编码。 二、算法思想 首先&#xff0c;打开文本文件&#xff0…