本篇博客是基于上一篇博客写出来的,了解上一篇博客
大家好,我是苏貝,本篇博客带大家再次优化上一篇的通讯录,实现将录入的数据在程序退出后存储到文件中,在下一次程序开始时打开文件获取数据,如果你觉得我写的还不错的话,可以给我一个赞👍吗,感谢❤️
目录
- 一. 保存通讯录到文件中
- 二. 在程序开始时加载文件信息到通讯录
- 三. 模块化代码实现
- contact.c
- test.c
- contact.h
- 结果演示
一. 保存通讯录到文件中
既然我们想在程序退出后将数据存储在文件中,那么在选择退出程序选项时,要实现SaveContact函数,它的功能就是保存通讯录到文件中
case EXIT:
printf("退出程序\n");
//保存通讯录到文件中
SaveContact(&con);
DestroyContact(&con);
break;
想要保存通讯录到文件中,首先需要打开一个文件,以“wb”形式打开文件,若文件存在,则直接打开文件。如文件不存在,则先建立该文件并打开。需要判断打开文件是否成功。成功后开始将数据写入文件中,使用fwrite二进制输出函数(将数据写入流中)了解fwrite函数
使用fwrite(&(pc->data[i]), sizeof(PeoInfo), 1, pf);或者fwrite(pc->data + i, sizeof(PeoInfo), 1, pf); 将一组数据写入流中,循环pc->sz次,可以将所有的数据都写入文件(因为每次使用完fwrite函数读取n个元素后,函数的指示器会向后移n个元素,即下次调用fwrite函数时,是从第n+1个元素开始读取的)
void SaveContact(Contact* pc)
{
FILE* pf = fopen("contact.txt", "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);
fwrite(pc->data + i, sizeof(PeoInfo), 1, pf);
}
fclose(pf);
pf = NULL;
}
二. 在程序开始时加载文件信息到通讯录
我们想要在程序开始时就读取文件中的通讯录,那么不妨将LoadContact(pc)函数放在初始化通讯录函数的最后。
//动态初始化通讯录
void InitContact(Contact* pc)
{
assert(pc);
pc->sz = 0;
pc->capacity = DEFAULT_CAP;
pc->data = (PeoInfo*)calloc(pc->capacity, sizeof(PeoInfo));
if (pc->data == NULL)
{
perror("InitContact_calloc");
return;
}
LoadContact(pc);
}
要读取文件,那么首先要打开文件,再判断是否打开成功,最后关闭文件。成功打开文件后,读取文件。因为前面是用fwrite函数将数据写入文件中,所以现在要用fread函数读取文件中的数据。用fread函数每次读取一个联系人的数据,读取成功,函数返回1,进去while循环,将tmp赋值给通讯录的元素,下标是pc->sz,最后sz++;读取失败,函数返回0,退出while循环。
PeoInfo tmp = { 0 };
while (fread(&tmp, sizeof(PeoInfo), 1, pf))
{
pc->data[pc->sz] = tmp;
pc->sz++;
}
你觉得这就好了吗?不是的,在将tmp赋值给通讯录前,我们要先判断通讯录的大小是否小于通讯录的最大容量,最大容量的初始值是3(如下),如果文件中的元素>=3,我们在进行赋值操作前就要先增容。
#define DEFAULT_CAP 3
增容的代码在动态增加联系人中,因为现在我们又需要增容操作,所以将增容的代码提取出来作为一个函数使用
//动态增加联系人
void AddContact(Contact* pc)
{
assert(pc);
//增容
if (pc->sz == pc->capacity)
{
PeoInfo* tmp = realloc(pc->data, (pc->capacity + DEFAAULT_INI) * sizeof(PeoInfo));
if (tmp == NULL)
{
perror("AddContact_realloc");
return;
}
pc->data = tmp;
pc->capacity += DEFAAULT_INI;
printf("增容成功\n");
}
printf("请输入名字:");
scanf("%s", pc->data[pc->sz].name);
printf("请输入年龄:");
scanf("%d", &(pc->data[pc->sz].age));
printf("请输入性别:");
scanf("%s", pc->data[pc->sz].gender);
printf("请输入电话:");
scanf("%s", pc->data[pc->sz].tele);
printf("请输入地址:");
scanf("%s", pc->data[pc->sz].addr);
pc->sz++;
printf("增加成功\n");
}
void CheckCapacity(Contact* pc)
{
if (pc->sz == pc->capacity)
{
PeoInfo* tmp = realloc(pc->data, (pc->capacity + DEFAAULT_INI) * sizeof(PeoInfo));
if (tmp == NULL)
{
perror("AddContact_realloc");
return;
}
pc->data = tmp;
pc->capacity += DEFAAULT_INI;
printf("增容成功\n");
}
}
还有一个问题,为什么要定义一个变量tmp来接收文件的数据而不是直接用pc->data[pc->sz]来接收,这也是为了避免通讯录容量不够而导致的错误
void LoadContact(Contact* pc)
{
FILE* pf = fopen("contact.txt", "rb");
if (pf == NULL)
{
perror("LoadContact");
return;
}
//读文件
PeoInfo tmp = { 0 };
while (fread(&tmp, sizeof(PeoInfo), 1, pf))
{
CheckCapacity(pc);
pc->data[pc->sz] = tmp;
pc->sz++;
}
fclose(pf);
pf = NULL;
}
三. 模块化代码实现
contact.c
#include"contact.h"
//静态初始化通讯录
//void InitContact(Contact* pc)
//{
// assert(pc);
// pc->sz = 0;
// memset(pc->data, 0, sizeof(pc->data));
//}
void CheckCapacity(Contact* pc);
void LoadContact(Contact* pc)
{
FILE* pf = fopen("contact.txt", "rb");
if (pf == NULL)
{
perror("LoadContact");
return;
}
//读文件
PeoInfo tmp = { 0 };
while (fread(&tmp, sizeof(PeoInfo), 1, pf))
{
CheckCapacity(pc);
pc->data[pc->sz] = tmp;
pc->sz++;
}
fclose(pf);
pf = NULL;
}
//动态初始化通讯录
void InitContact(Contact* pc)
{
assert(pc);
pc->sz = 0;
pc->capacity = DEFAULT_CAP;
pc->data = (PeoInfo*)calloc(pc->capacity, sizeof(PeoInfo));
if (pc->data == NULL)
{
perror("InitContact_calloc");
return;
}
LoadContact(pc);
}
//静态增加联系人
//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].gender);
// printf("请输入电话:");
// scanf("%s", pc->data[pc->sz].tele);
// printf("请输入地址:");
// scanf("%s", pc->data[pc->sz].addr);
//
// pc->sz++;
// printf("增加成功\n");
//}
void CheckCapacity(Contact* pc)
{
if (pc->sz == pc->capacity)
{
PeoInfo* tmp = realloc(pc->data, (pc->capacity + DEFAAULT_INI) * sizeof(PeoInfo));
if (tmp == NULL)
{
perror("AddContact_realloc");
return;
}
pc->data = tmp;
pc->capacity += DEFAAULT_INI;
printf("增容成功\n");
}
}
//动态增加联系人
void AddContact(Contact* pc)
{
assert(pc);
//增容
CheckCapacity(pc);
printf("请输入名字:");
scanf("%s", pc->data[pc->sz].name);
printf("请输入年龄:");
scanf("%d", &(pc->data[pc->sz].age));
printf("请输入性别:");
scanf("%s", pc->data[pc->sz].gender);
printf("请输入电话:");
scanf("%s", pc->data[pc->sz].tele);
printf("请输入地址:");
scanf("%s", pc->data[pc->sz].addr);
pc->sz++;
printf("增加成功\n");
}
int FindOfName(char* name, Contact* pc)
{
assert(name && pc);
int i = 0;
for (i = 0; i < pc->sz; i++)
{
if (0 == strcmp(name, (pc->data[i].name)))
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 = FindOfName(name, pc);
if (ret == -1)
{
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");
}
//打印通讯录
void ShowContact(Contact* pc)
{
assert(pc);
if (pc->sz == 0)
{
printf("通讯录为空,打印无意义\n");
return;
}
int i = 0;
printf("%-20s%-5s%-5s%-30s%-30s\n", "姓名", "年龄", "性别", "电话", "地址");
for (i = 0; i < pc->sz; i++)
{
printf("%-20s%-5d%-5s%-30s%-30s\n", pc->data[i].name, pc->data[i].age,
pc->data[i].gender, pc->data[i].tele, pc->data[i].addr);
}
}
//查找联系人
void SelectContact(Contact* pc)
{
char name[NAME_MAX] = { 0 };
printf("请输入要查找的人的名字:");
scanf("%s", name);
int ret = FindOfName(name, pc);
if (ret == -1)
{
printf("找不到要查找的人\n");
return;
}
printf("%-20s%-5s%-5s%-30s%-30s\n", "姓名", "年龄", "性别", "电话", "地址");
printf("%-20s%-5d%-5s%-30s%-30s\n", pc->data[ret].name, pc->data[ret].age,
pc->data[ret].gender, pc->data[ret].tele, pc->data[ret].addr);
}
//修改联系人信息
void ModifyContact(Contact* pc)
{
assert(pc);
char name[NAME_MAX] = { 0 };
printf("请输入要修改的人的名字:");
scanf("%s", name);
int ret = FindOfName(name, pc);
if (ret == -1)
{
printf("找不到要修改的人\n");
return;
}
printf("请输入名字:");
scanf("%s", pc->data[ret].name);
printf("请输入年龄:");
scanf("%d", &(pc->data[ret].age));
printf("请输入性别:");
scanf("%s", pc->data[ret].gender);
printf("请输入电话:");
scanf("%s", pc->data[ret].tele);
printf("请输入地址:");
scanf("%s", pc->data[ret].addr);
}
int cmp(const void* a, const void* b)
{
return strcmp((char*)a, (char*)b);
}
//以名字排序所有联系人
void SortContact(Contact* pc)
{
assert(pc);
qsort(pc->data, pc->sz, sizeof(pc->data[0]), cmp);
printf("排序成功\n");
}
//销毁通讯录
void DestroyContact(Contact* pc)
{
free(pc->data);
pc->data = NULL;
}
void SaveContact(Contact* pc)
{
FILE* pf = fopen("contact.txt", "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);
fwrite(pc->data + i, sizeof(PeoInfo), 1, pf);
}
fclose(pf);
pf = NULL;
}
test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"contact.h"
void menu()
{
printf("-----------------------------------\n");
printf("***********************************\n");
printf("****** 1.Add 2.Del ******\n");
printf("****** 3.Select 4.Modify ******\n");
printf("****** 5.Show 6.sort ******\n");
printf("****** 0.exit ******\n");
printf("***********************************\n");
printf("-----------------------------------\n");
}
enum Option
{
EXIT,
ADD,
DEL,
SELECT,
MODIFY,
SHOW,
SORT
};
int main()
{
Contact con;
//初始化通讯录
InitContact(&con);
int input = 0;
do
{
menu();
printf("请选择:");
scanf("%d", &input);
switch (input)
{
case ADD:
AddContact(&con);
break;
case DEL:
DelContact(&con);
break;
case SELECT:
SelectContact(&con);
break;
case MODIFY:
ModifyContact(&con);
break;
case SHOW:
ShowContact(&con);
break;
case SORT:
SortContact(&con);
break;
case EXIT:
printf("退出程序\n");
//保存通讯录到文件中
SaveContact(&con);
DestroyContact(&con);
break;
default:
printf("选择错误,请重新选择\n");
break;
}
} while (input);
return 0;
}
contact.h
#pragma once
#include<stdio.h>
#include<assert.h>
#include<string.h>
#include<stdlib.h>
#define NAME_MAX 20
#define GENDER_MAX 5
#define TELE_MAX 12
#define ADDR_MAX 30
//#define MAX 1000
#define DEFAULT_CAP 3
#define DEFAAULT_INI 2
typedef struct PeoInfo
{
char name[NAME_MAX];
int age;
char gender[GENDER_MAX];
char tele[TELE_MAX];
char addr[ADDR_MAX];
}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 DelContact(Contact* pc);
//打印通讯录
void ShowContact(Contact* pc);
//查找联系人
void SelectContact(Contact* pc);
//修改联系人信息
void ModifyContact(Contact* pc);
//以名字排序所有联系人
void SortContact(Contact* pc);
//销毁通讯录
void DestroyContact(Contact* pc);
//判断是否需要增容
void CheckCapacity(Contact* pc);
//保存通讯录到文件中
void SaveContact(Contact* pc);
//初始化时读取文件
void LoadContact(Contact* pc);
结果演示
第一次运行程序
第二次运行程序
在程序运行时就加载文件信息到通讯录,因为文件中有4个联系人的信息,所以显示增容成功
好了,那么本篇博客就到此结束了,如果你觉得本篇博客对你有些帮助,可以给个大大的赞👍吗,感谢看到这里,我们下篇博客见❤️