我们在前文中介绍了如何实现简易通讯录
后来又改进了通讯录,使之可以进行动态内存管理。
但是,我们的通讯录程序是只能一次性使用的,退出程序所有的内容都消失了,那么本文就来实现将通讯录的数据写入到硬盘上的文件。再次打开程序时,再从文件上读取数据。实现数据的持久性。
代码如下:
#pragma once
//头文件
#include
#include
#include
#define MAX_NAME 20
#define MAX_SEX 6
#define MAX_TELE 12
#define MAX_ADDR 20
#define MAX 1000
#define INIT_SZ 3
#define CAP_ADD 2
enum fun
{
EXIT,//从0开始,EXIT = 0
ADD,//1
DEL,//2
SEARCH,//3
MODIFY,//4
PRINT,
SORT,
DESTORY
};
//个人信息结构体:
typedef struct PeoInfo
{
char name[MAX_NAME];
int age;
char sex[MAX_SEX];
char tele[MAX_TELE];
char addr[MAX_ADDR];
}PeoInfo;
通讯录结构体:静态版本
//typedef struct Book
//{
// PeoInfo data[MAX];//
// int size;//记录当前通讯录中有效的信息个数
//}Book;
//通讯录结构体:动态版本
typedef struct Book
{
PeoInfo* data;//动态版本:记录个人信息
int size;//记录当前通讯录中有效的信息个数
int cap;//记录当前的最大容量,以便满了通过动态内存管理来扩充
}Book;
//菜单函数声明:
void menu(void);
void menu_ins(void);
//初始化通讯录函数声明
void InitMsg(Book*);
//增加通讯录信息函数声明:
void AddMsg(Book* );
//打印通讯录信息函数声明:
void PrintMsg(Book* );
//删除通讯录信息函数声明:
void DelMsg(Book* );
//查找通讯录信息函数声明:
void SearchMsg(Book*);
//修改通讯录信息函数声明:
void ModMsg(Book*);
//排序通讯录信息函数声明:
void SortMsg(Book*);
//通讯录动态空间释放函数声明:
void DestroyMsg(Book*);
//检查是否需要增容:
void CheckCap(Book* pc);
//保存通讯录信息到文件中的函数声明:
void SaveMsg(Book*);
//程序打开,初始化通讯录时,加载文件中的数据到通讯录中函数声明:
void LoadMsg(Book*);
//删除文件中通讯录信息,清空通讯录文件
void nothing(Book* pc);
//
//本文件包括 各功能函数:
#include "addressBook.h"
//菜单显示:
void menu()
{
printf("********************************\n");
printf("****** 1.增加 2.删去 ******\n");
printf("****** 3.搜索 4.修改 *****\n");
printf("****** 5.浏览 6.排序 *****\n");
printf("****** 0.退出 7.销毁 *******\n");
printf("********************************\n");
}
//菜单说明:
void menu_ins()
{
printf("菜单说明\n");
printf("增加:增加通讯录信息\n");
printf("删去:输入姓名,删去该姓名对应的通讯录信息\n");
printf("搜索:输入姓名,搜索并打印该姓名对应的通讯录信息\n");
printf("修改:输入姓名,修改该姓名对应的通讯录信息\n");
printf("浏览:打印出全部通讯录信息\n");
printf("排序:根据姓名/年龄排序通讯录信息\n");
printf("销毁:清空通讯录及文件中的数据信息\n");
}
//通过姓名来寻找某条通讯录信息函数:
static int FindByName(Book* pc, char* name)
{
for (int i = 0; i < pc->size; i++)
{
if (strcmp(name, pc->data[i].name) == 0)//strcmp来判断是不是相同
{
return i;
}
}
return -1;//没有找到,返回-1
}
//初始化时加载文件中的数据到通讯录中:
void LoadMsg(Book* pc)
{
//打开文件
FILE* pf = fopen("addressBook.txt", "r");
//检查文件打开成功与否
if (pf == NULL)
{
perror("LoadMsg");
printf("加载文件出错!\n");
}
//开始加载数据:
PeoInfo tmp = { 0 };
//巧妙使用fread函数返回值来一直读取数据
while (fread(&tmp, sizeof(PeoInfo), 1, pf))
{
//是否需要增容
CheckCap(pc);
pc->data[pc->size] = tmp;
pc->size++;
}
//关闭文件
fclose(pf);
pf = NULL;
printf("加载文件成功!\n");
}
//通讯录初始化函数:
//和静态版本不同,这里data没有初始化大小,因此需要在这个初始化函数中给它分配一个动态空间
void InitMsg(Book* pc)
{
pc->data = (PeoInfo*)malloc(INIT_SZ * sizeof(PeoInfo));
if (pc->data == NULL)
{
perror("初始化函数InitMsg出现错误\n");
return;
}
pc->size = 0;
pc->cap = INIT_SZ;
//打开程序,初始化时从文件中加载数据:
LoadMsg(pc);
}
//销毁通讯录,清空文件信息
void nothing(Book* pc)
{
//打开文件以写的形式
FILE* pf = fopen("addressBook.txt", "w");
//检查打开成功与否
if (pf == NULL)
{
perror("SaveMsg");
printf("保存失败!\n");
return;
}
//清空通讯录信息
//每次保存一条空信息进去,循环保存pc->size次将所有信息清楚
PeoInfo tmp = { 0 };
for (int i = 0; i < pc->size; i++)
{
fwrite(&tmp, sizeof(PeoInfo), 1, pf);
}
pc->size = 0;//通信录信息计数清零
pc->cap = INIT_SZ;//通讯录大小恢复初始值
fclose(pf);
pf = NULL;
printf("文件已销毁\n");
}
增加通讯录信息功能函数:静态版本:
//void AddMsg(Book* pc)
//{
// printf("请输入姓名:");
// scanf("%s", pc->data[pc->size].name);
// printf("请输入年龄:");
// scanf("%d", &(pc->data[pc->size].age));
// printf("请输入性别:");
// scanf("%s", pc->data[pc->size].sex);
// printf("请输入电话:");
// scanf("%s", pc->data[pc->size].tele);
// printf("请输入地址:");
// scanf("%s", pc->data[pc->size].addr);
//
// pc->size++;//增加一个后,通讯录内的下表要自加1,以填入下一个信息。否则以前的信息将被覆盖。
//
// printf("增加成功\n");
//
//}
//检查是否需要增容:
void CheckCap(Book* pc)
{
if (pc->size == pc->cap)//容量达到当前最大容量,需要扩容
{
PeoInfo* ptr = realloc(pc->data, (CAP_ADD + pc->cap) * sizeof(PeoInfo));//realloc函数第二个参数是原空间+需要调整的大小
if (ptr != NULL)
{
pc->data = ptr;
pc->cap += CAP_ADD;
printf("扩容成功!\n");
}
else
{
perror("通讯录扩容出现错误\n");
printf("扩容失败!\n");
}
}
}
//增加通讯录信息功能函数:动态版本:
void AddMsg(Book* pc)
{
CheckCap(pc);
printf("请输入姓名:");
int rescanf = scanf("%s", pc->data[pc->size].name);
printf("请输入年龄:");
rescanf = scanf("%d", &(pc->data[pc->size].age));
printf("请输入性别:");
rescanf = scanf("%s", pc->data[pc->size].sex);
printf("请输入电话:");
rescanf = scanf("%s", pc->data[pc->size].tele);
printf("请输入地址:");
rescanf = scanf("%s", pc->data[pc->size].addr);
pc->size++;//增加一个后,通讯录内的下表要自加1,以填入下一个信息。否则以前的信息将被覆盖。
printf("增加成功\n");
}
//打印浏览通讯录信息功能函数:
void PrintMsg(Book* pc)
{
if (pc->size == 0)//判断通讯录是不是空的
{
printf("通讯录为空!\n");
return;
}
//打印标题栏:
printf("%-10s\t%-4s\t%-6s\t%-12s\t%-20s\n", "姓名", "年龄", "性别", "电话", "地址");
//打印内容:
for (int i = 0; i < pc->size; i++)
{
printf("% -10s\t% -4d\t% -6s\t% -12s\t% -20s\n", pc->data[i].name,
pc->data[i].age,
pc->data[i].sex,
pc->data[i].tele,
pc->data[i].addr);
}
printf("打印成功!\n");
}
//删除通讯录信息功能函数:
void DelMsg(Book* pc)
{
if (pc->size == 0)//判断通讯录是不是空的
{
printf("通讯录为空,无需删除\n");
return;
}
char name[MAX_NAME] = { 0 };
printf("请输入要删除的人的姓名:\n");
int rescanf = scanf("%s", name);
//1.查找这个名字:
int ret = FindByName(pc, name);
//找不到:
if (ret == -1)
{
printf("查无此人\n");
return;
}
//找到了
//2.开始删除:
else
{
for (int i = ret; i < pc->size - 1; i++)
{
pc->data[i] = pc->data[i + 1];//删除其实就是让后一个向前覆盖
}
}
pc->size--;//因为删除了一个,所以pc->size也相应-1
printf("删除成功!\n");
}
//查找并打印通讯录某一条信息功能函数:
void SearchMsg(Book* pc)
{
if (pc->size == 0)//判断通讯录是不是空的
{
printf("通讯录为空!\n");
return;
}
char name[MAX_NAME] = { 0 };
printf("请输入要查找的人的姓名:\n");
int rescanf = scanf("%s", name);
//1.查找这个名字:
int i = FindByName(pc, name);
//找不到:
if (i == -1)
{
printf("查无此人\n");
return;
}
//找到了
//2.开始打印:
else
{
printf("查找成功,信息如下>\n");
printf("%-10s\t%-4s\t%-6s\t%-12s\t%-20s\n", "姓名", "年龄", "性别", "电话", "地址");
printf("% -10s\t% -4d\t% -6s\t% -12s\t% -20s\n", pc->data[i].name,
pc->data[i].age,
pc->data[i].sex,
pc->data[i].tele,
pc->data[i].addr);
}
printf("查找成功!\n");
}
//修改某人信息功能函数:
void ModMsg(Book* pc)
{
if (pc->size == 0)//判断通讯录是不是空的
{
printf("通讯录为空!\n");
return;
}
char name[MAX_NAME] = { 0 };
printf("请输入要修改的人的姓名:\n");
int rescanf = scanf("%s", name);
//1.查找这个名字:
int i = FindByName(pc, name);
//找不到:
if (i == -1)
{
printf("查无此人\n");
return;
}
else
{
printf("请输入修改后姓名:");
rescanf = scanf("%s", pc->data[i].name);
printf("请输入修改后年龄:");
rescanf = scanf("%d", &(pc->data[i].age));
printf("请输入修改后性别:");
rescanf = scanf("%s", pc->data[i].sex);
printf("请输入修改后电话:");
rescanf = scanf("%s", pc->data[i].tele);
printf("请输入修改后地址:");
rescanf = scanf("%s", pc->data[i].addr);
printf("修改成功\n");
}
}
//排序功能实现:
//根据姓名或者年龄排:
//
//1.根据姓名排:
static int sort_name(const void* e1, const void* e2)
{
return strcmp(((PeoInfo*)e1)->name, ((PeoInfo*)e2)->name);
}
static void SortByName(Book* pc)
{
qsort(pc->data,pc->size, sizeof(pc->data[0]), sort_name);
//此处第二个参数是要传需排序的元素的个数,也就是有多少条通讯录信息
//因为我们专卖在结构体Book里加入了计数变量size,所以此处传size即可
}
//2.根据年龄排:
static int sort_age(const void* e1, const void* e2)
{
return ((PeoInfo*)e1)->age - ((PeoInfo*)e2)->age;
}
static void SortByAge(Book* pc)
{
qsort(pc->data,pc->size , sizeof(pc->data[0]), sort_age);
}
//排序函数:
void SortMsg(Book* pc)
{
if (pc->size == 0)//判断通讯录是不是空的
{
printf("通讯录为空!\n");
return;
}
int input = 0;
printf("****** 请选择排序方式 **********\n");
printf("******** 1.姓名 ************\n");
printf("******** 2.年龄 ************\n");
printf("********************************\n");
int rescanf = scanf("%d", &input);
switch (input)
{
case 1:
SortByName(pc);
break;
case 2:
SortByAge(pc);
break;
}
}
//通讯录动态空间释放函数声明:
void DestroyMsg(Book* pc)
{
free(pc->data);
pc->data = NULL;
pc->size = 0;
pc->cap = 0;
printf("退出通讯录\n");
}
//保存通讯录信息到文件中:
void SaveMsg(Book* pc)
{
//打开文件以写的形式
FILE* pf = fopen("addressBook.txt", "w");
//检查打开成功与否
if (pf == NULL)
{
perror("SaveMsg");
printf("保存失败!\n");
return;
}
//保存通讯录信息
//每次保存一条,循环保存pc->size次保存完整
for (int i = 0; i < pc->size; i++)
{
fwrite(&pc->data[i], sizeof(PeoInfo), 1, pf);
}
fclose(pf);
pf = NULL;
printf("文件已保存\n");
}
//要求:
//
//1. 该通讯录能存放1000个人的信息。每个人的信息包括:姓名 + 年龄 + 性别 + 电话 + 地址;
//2. 增加通讯录信息;
//3. 删去通讯录信息;
//4. 查找通讯录信息;
//5. 修改通讯录信息;
//6. 打印浏览通讯录信息。
#include "addressBook.h"
int main()
{
int input = 0;
Book myBook ;
InitMsg(&myBook);//定义一个通讯录并初始化
menu_ins();
do {
menu();//打印菜单,以供选择
printf("请选择功能 >\n");
int rescanf = scanf("%d", &input);
switch (input)
{
//1.增加通讯录信息:
case ADD:
AddMsg(&myBook);
break;
//2.删除通讯录信息:
case DEL:
DelMsg(&myBook);
break;
//3.查找通讯录信息:
case SEARCH:
SearchMsg(&myBook);
break;
//4.修改通讯录信息:
case MODIFY:
ModMsg(&myBook);
break;
//5.打印浏览通讯录信息:
case PRINT:
PrintMsg(&myBook);
break;
//6.根据姓名/年龄/性别排序:
case SORT:
SortMsg(&myBook);
break;
//退出程序:
case EXIT:
SaveMsg(&myBook);//保存通讯录信息到文件
DestroyMsg(&myBook);//释放通讯录动态内存空间
break;
//销毁通讯录文件信息
case DESTORY:
nothing(&myBook);
break;
default:
printf("输入错误,重新输入>\n");
break;
}
} while (input);
}