C - 通讯录3.0

目录

一.保存数据

二.加载文件信息到通讯录里

三.整体代码

1.test.c

2.contact.h

3.contact.c


1.0,2.0版本的通讯录,运行起来的时候,可以给通讯录中增加、删除数据。此时数据是存放在内存中,当程序退出的时候,通讯录中的数据自然就不存在了。等下次运行通讯录程序的时候,数据又得重新录入,如果使用这样的通讯录就很难受。3.0版本为大家解决这一问题。

一.保存数据

我们按0退出时,不要直接销毁。应该先保存通讯录信息到文件中,再销毁。

cpp 复制代码
//test.c
case EXIT:
	//保存通讯录信息到文件中
	SaveContact(&con);
	DestroyContact(&con);
	printf("退出通讯录\n");
//contact.h
void SaveContact(Contact* pc);
cpp 复制代码
void SaveContact(Contact* pc)
{
	assert(pc);
	//打开文件
	FILE* pf = fopen("contact.txt", "wb");
	if (NULL == pf)
	{
		perror("SaveContact");
	}
	else
	{
		//写数据

		//关闭文件
		fclose(pf);
		pf = NULL;
	}
}

用 wb 形式打开文件,每次文件都是重新创建的。

比如:原来放了4个人,现在新加了2个,一共6个。下次保存时,把原来的文件清空,再把这6个重新放进去。
不会形成数据冲突、重复

fread fwrite 函数可以写1个或多个数据到文件中,用它俩。



将 ptr 指向的,要被写的 count 个,每个 size 字节的 数据,写入 FILE* (这里是 pf )指向的文件中。
返回成功写入的元素总数。


我们每次写1个数据,有多少个就写多少次。

每次要被写进文件里的是 pc->data[ i ] 。fwrite 要的是地址,pc->data[ i ] 的 地址是:pc->data + i

cpp 复制代码
//contact.c
void SaveContact(Contact* pc)
{
    assert(pc);
	//打开文件
	FILE* pf = fopen("contact.txt", "wb");
	if (NULL == pf)
	{
		perror("SaveContact");
	}
	else
	{
		//写数据
		int i = 0;
		for (i = 0; i < pc->sz; i++)
		{
			fwrite(pc->data + i, sizeof(PeoInfo), 1, pf);
		}
		//关闭文件
		fclose(pf);
		pf = NULL;
		printf("保存成功\n");
	}
}

二.加载文件信息到通讯录里

下次运行程序,要能看到上次保存好的数据。
程序运行时,第一步就是初始化,所以我们修改它。

cpp 复制代码
//动态版本  contact.c
void InitContact(Contact* pc)
{
	assert(pc);
	pc->sz = 0;
	PeoInfo* ptr = (PeoInfo*)calloc(DEFAULT_SZ, sizeof(PeoInfo));
	if (ptr == NULL)
	{
		perror("InitContact:calloc");
		return;
	}
	pc->data = ptr;
	pc->capacity = DEFAULT_SZ;
	printf("当前联系人数量:%d\n", pc->sz);
	printf("当前最大容量:%d\n", pc->capacity);

	//加载文件信息到通讯录
	LoadContact(pc);
}

//contact.h
void LoadContact(Contact* pc);

把数据放到 pc 指向的通讯录里。所以把 pc 传进去。


cpp 复制代码
void LoadContact(Contact* pc)
{
	assert(pc);
	//打开文件
	FILE* pf = fopen("contact.txt", "rb");
	if (pf == NULL)
	{
		perror("LoadContact");
	}
	else
	{
		//读数据


		//关闭文件
		fclose(pf);
		pf = NULL;
	}
}

从文件中读数据。

通讯录刚加载起来,文件里有多少个联系人我不知道。

**怎么读?读到什么时候结束?**看 fread 什么时候读取失败



从 FILE* (这里指 pf )指向的文件中,读取 count 个数据,每个数据 size 个字节。并将它们存储在 ptr指定的内存块中。
返回成功读取的元素总数。


从哪个文件里读?我们清楚。

读多少个?

eg:每次读5个,如果正确读到5个,返回5 。

但有可能读不到5

eg:文件中有8个联系人,每次读5个。第一次读到5个是正常的,第二次最多读到3个就返回了,不读了。

如果 实际读到的个数 < 每次要读的个数,这就是最后一次读。
我们每次让它读一个,正常读1个返回1。读到不是一个(读取结束),返回0。放到 tmp 中

cpp 复制代码
	else
	{
		//读数据
		PeoInfo tmp = { 0 };
		fread(&tmp, sizeof(PeoInfo), 1, pf);
        
        //关闭文件
	}

用 while 循环起来,判断条件上面分析好了。

cpp 复制代码
	else
	{
		//读数据
		PeoInfo tmp = { 0 };
		int i = 0;
		while (fread(&tmp, sizeof(PeoInfo), 1, pf))
		{
			pc->data[i] = tmp;
			pc->sz++;
			i++;
		}

        //关闭文件
	}

最开始通讯录只准备好了3个空间。

假设文件里有5个联系人?要考虑扩容的问题。我们分装了函数。

cpp 复制代码
void LoadContact(Contact* pc)
{
	assert(pc);
	//打开文件
	FILE* pf = fopen("contact.txt", "rb");
	if (pf == NULL)
	{
		perror("LoadContact");
	}
	else
	{
		//读数据
		PeoInfo tmp = { 0 };
		int i = 0;
		while (fread(&tmp, sizeof(PeoInfo), 1, pf))
		{
			//扩容
			check_capacity(pc);

			pc->data[i] = tmp;
			pc->sz++;
			i++;
		}
		//关闭文件
		fclose(pf);
		pf = NULL;
	}
}

三.整体代码

1.test.c

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS 1
#include "contact.h"

void menu()
{
	printf("************************************\n");
	printf("******  1. add    2. del      ******\n");
	printf("******  3. search 4. modify   ******\n");
	printf("******  5. show   6. sort     ******\n");
	printf("******  7. delall 0. exit     ******\n");
	printf("************************************\n");
}

enum Option
{
	EXIT,//0
	ADD, //1
	DEL, //2
	SEARCH,
	MODIFY,
	SHOW,
	SORT,
	DELALL
};

int main()
{
	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:
			SortContact_ByName(&con);
			break;
		case DELALL:
			DelAllContact(&con);
			break;
		case EXIT:
			//保存通讯录信息到文件中
			SaveContact(&con);
			DestroyContact(&con);
			printf("退出通讯录\n");
			break;
		default:
			printf("输入错误\n");
			break;
		}

	} while (input);

	return 0;
}

2.contact.h

cpp 复制代码
#pragma once
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <stdlib.h>

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

#define DEFAULT_SZ 3 //默认大小
#define INC_SZ 2 //每次增加
#define RED_SZ 3 //每次减少
#define DIFF_SZ_CAPA 5 //最大 - 当前 的差值

//人的信息
typedef struct PeoInfo
{
	char name[NAME_MAX];
	int age;
	char sex[SEX_MAX];
	char addr[ADDR_MAX];
	char tele[TELE_MAX];
}PeoInfo;

//动态版本
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 DelAllContact(Contact* pc);

//销毁内存空间
void DestroyContact(Contact* pc);

//根据姓名排序
void SortContact_ByName(Contact* pc);

//保存通讯录信息到文件中
void SaveContact(Contact* pc);

//加载文件信息到通讯录里
void LoadContact(Contact* pc);

3.contact.c

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS 1
#include "contact.h"

//动态版本
void InitContact(Contact* pc)
{
	assert(pc);
	pc->sz = 0;
	PeoInfo* ptr = (PeoInfo*)calloc(DEFAULT_SZ, sizeof(PeoInfo));
	if (ptr == NULL)
	{
		perror("InitContact:calloc");
		return;
	}
	pc->data = ptr;
	pc->capacity = DEFAULT_SZ;

	//加载文件信息到通讯录
	LoadContact(pc);
}

void check_capacity(Contact* pc)
{
	assert(pc);
	if (pc->sz == pc->capacity)
	{
		//扩容
		PeoInfo* ptr = (PeoInfo*)realloc(pc->data, (pc->capacity + INC_SZ) * sizeof(PeoInfo));
		if (ptr == NULL)
		{
			perror("check_capacity:realloc");
			return;
		}
		pc->data = ptr;
		pc->capacity += INC_SZ;
	}

	if ((pc->capacity - pc->sz) > DIFF_SZ_CAPA)
	{
		//减容
		PeoInfo* ptr = (PeoInfo*)realloc(pc->data, (pc->capacity - RED_SZ) * sizeof(PeoInfo));
		if (ptr == NULL)
		{
			perror("check_capacity:realloc");
			return;
		}
		pc->data = ptr;
		pc->capacity -= RED_SZ;
	}
}

//动态版本
void AddContact(Contact* pc)
{
	assert(pc);

	//增加一个人的信息
	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].addr);
	printf("请输入电话:");
	scanf("%s", pc->data[pc->sz].tele);

	pc->sz++;
	check_capacity(pc);
}

void ShowContact(const Contact* pc)
{
	assert(pc);
	printf("%-20s\t%-4s\t%-5s\t%-20s\t%-12s\n", "名字", "年龄", "性别", "地址", "电话");
	int i = 0;
	for (i = 0; i < pc->sz; i++)
	{
		printf("%-20s\t%-4d\t%-5s\t%-20s\t%-12s\n", pc->data[i].name,
			pc->data[i].age,
			pc->data[i].sex,
			pc->data[i].addr,
			pc->data[i].tele);
	}
}

int FindByName(const Contact* pc, char name[])
{
	assert(pc);

	int i = 0;
	for (i = 0; i < pc->sz; i++)
	{
		if (strcmp(pc->data[i].name, name) == 0)
		{
			return i;
		}
	}
	return -1;
}

//动态版本
void DelContact(Contact* pc)
{
	assert(pc);

	char name[NAME_MAX] = { 0 };
	if (pc->sz == 0)
	{
		printf("通讯录为空,无法删除\n");
		return;
	}
	//查找
	printf("请输入要删除的人的名字:>");
	scanf("%s", name);
	int ret = FindByName(pc, name);
	if (-1 == ret)
	{
		printf("要删除的人不存在\n");
		return;
	}

	int i = 0;
	//删除
	for (i = ret; i < pc->sz - 1; i++)
	{
		pc->data[i] = pc->data[i + 1];
	}
	pc->sz--;
	printf("删除成功\n");
	check_capacity(pc);
}

void SearchContact(const Contact* pc)
{
	assert(pc);
	char name[NAME_MAX] = { 0 };
	printf("请输入要查找人的名字:>");
	scanf("%s", name);
	int pos = FindByName(pc, name);
	if (-1 == pos)
	{
		printf("要查找的人不存在\n");
		return;
	}
	//打印信息
	printf("%-20s\t%-4s\t%-5s\t%-20s\t%-12s\n", "名字", "年龄", "性别", "地址", "电话");
	printf("%-20s\t%-4d\t%-5s\t%-20s\t%-12s\n", pc->data[pos].name,
		pc->data[pos].age,
		pc->data[pos].sex,
		pc->data[pos].addr,
		pc->data[pos].tele);
}

void ModifyContact(Contact* pc)
{
	assert(pc);
	char name[NAME_MAX] = { 0 };
	printf("请输入要修改人的名字:");
	scanf("%s", name);
	int pos = FindByName(pc, name);
	if (-1 == pos)
	{
		printf("要修改的人不存在\n");
		return;
	}

	//重新录入信息
	printf("请输入名字:");
	scanf("%s", pc->data[pos].name);
	printf("请输入年龄:");
	scanf("%d", &(pc->data[pos].age));
	printf("请输入性别:");
	scanf("%s", pc->data[pos].sex);
	printf("请输入地址:");
	scanf("%s", pc->data[pos].addr);
	printf("请输入电话:");
	scanf("%s", pc->data[pos].tele);

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

void DelAllContact(Contact* pc)
{
	assert(pc);
	pc->sz = 0;
	memset(pc->data, 0, sizeof(pc->data));

	printf("删除成功。\n");
}

void DestroyContact(Contact* pc)
{
	assert(pc);
	free(pc->data);
	pc->data = NULL;
	pc->capacity = 0;
	pc->sz = 0;
	pc = NULL;
}

int sortcon_byname(const void* e1, const void* e2)
{
	return strcmp(((PeoInfo*)e1)->name, ((PeoInfo*)e2)->name);
}

void SortContact_ByName(Contact* pc)
{
	assert(pc);
	qsort(pc->data, pc->sz, sizeof(PeoInfo), sortcon_byname);
	printf("排序成功\n");
}

void SaveContact(Contact* pc)
{
	assert(pc);
	//打开文件
	FILE* pf = fopen("contact.txt", "wb");
	if (NULL == pf)
	{
		perror("SaveContact");
	}
	else
	{
		//写数据
		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 LoadContact(Contact* pc)
{
	assert(pc);
	//打开文件
	FILE* pf = fopen("contact.txt", "rb");
	if (pf == NULL)
	{
		perror("LoadContact");
	}
	else
	{
		//读数据
		PeoInfo tmp = { 0 };
		int i = 0;
		while (fread(&tmp, sizeof(PeoInfo), 1, pf))
		{
			//扩容
			check_capacity(pc);

			pc->data[i] = tmp;
			pc->sz++;
			i++;
		}
		//关闭文件
		fclose(pf);
		pf = NULL;
	}
}

通讯录3个版本到这里就全部实现了。

本篇的分享就到这里了,感谢观看 ,如果对你有帮助,别忘了点赞+收藏+关注

小编会以自己学习过程中遇到的问题为素材,持续为您推送文章。

相关推荐
Android洋芋5 小时前
C语言深度解析:从零到系统级开发的完整指南
c语言·开发语言·stm32·条件语句·循环语句·结构体与联合体·指针基础
Run_Teenage6 小时前
C语言 【初始指针】【指针一】
c语言·开发语言
涛ing7 小时前
【Git “fetch“ 命令详解】
linux·c语言·c++·人工智能·git·vscode·svn
_GR9 小时前
2022年蓝桥杯第十三届C&C++大学B组真题及代码
c语言·数据结构·c++·算法·蓝桥杯·动态规划
双叶83610 小时前
(C语言)学生信息表(学生管理系统)(基于通讯录改版)(正式版)(C语言项目)
c语言·开发语言·c++·算法·microsoft
小麦嵌入式11 小时前
Linux驱动开发实战(九):Linux内核pinctrl_map详解与优势分析
linux·c语言·汇编·驱动开发·stm32·嵌入式硬件·硬件工程
阿巴~阿巴~12 小时前
2023年3月全国计算机等级考试真题(二级C语言)
c语言
i love you china14 小时前
深入理解指针5
c语言
卷卷的小趴菜学编程14 小时前
算法篇-------------双指针法
c语言·开发语言·c++·vscode·算法·leetcode·双指针法
XWXnb615 小时前
C语言:多线程
c语言·开发语言