用文件操作升级通讯录
- 前言
- 1.实现逻辑
- 2.用哪种文件存储数据
- 2. `save_contact`函数设计
- 3. `load_contact` 函数设计
- 5.代码总览
- contact.h
- contact.c
- text.c
前言
在有关通讯录的上一篇博客中,作者用柔性数组实现了动态改变通讯录占用空间的功能,但是在最后还是留下了一个问题,那就是由于通讯录中的内容是存放在内存中的,在程序结束之后就会全部消失,所以这个通讯录还是没有使用价值。
我们的想法是既然是通讯录就应该把里面的内容记录下来,只有当我们自己选择删除数据的时候,数据才无法找回。
这就涉及到了数据持久化的问题,我们一般数据持久化的方法有,把数据存放在磁盘文件、存放到数据库等方式。
本篇博客将使用文件将数据直接存放在电脑的硬盘上,做到了数据的持久化。
由于使用了c语言文件操作的相关内容,如果有对文件操作不熟悉的小伙伴,建议先看看博主有关文件操作的博客,这样效果更好!
链接 : c语言文件操作
另外,由于该次通讯录的改进只改变一部分操作,大体实现不变,因此不再次细讲普通通讯录的实现细节,如果想具体看实现细节的可以看博主之前的博客:
简单通讯录链接: 简单通讯录实现(C语言)—在添加联系人的时候完成了排序工作,并且用二分查找方式寻找联系人
动态通讯录链接:动态通讯录实现——运用了柔性数组,并且加入了增容和缩容处理
好了,前言到此为止,现在进入正题,通讯录max实现!
1.实现逻辑
首先,我们考虑以下怎么将数据添加到文件中,这里我想到了两种办法:
- 每次添加或删除联系人同时更改文件内的内容
- (借用文件缓冲区的思路)在程序退出执行时再将内存中创建的通讯录内的内容全部写入文件中。
这里作者选择了第二种方法,有两个原因。
- 第一个方式需要频繁的进行文件操作,而第二个方式只需要进行一次文件操作即可。
- 第二种方式的实现更加简单(doge)
那么,整体逻辑就出来了,每次启动将文件中的内容全部输入程序运行时创建的通讯录中,程序结束时再将修改后的通讯录写入文件中。
这里作者设计了两个函数,
a. save_contact
函数在退出通讯录的时候把信息到保存到文件中
b. load_contact
函数在通讯录打开的时候,可以把文件中的信息加载到通讯录中。
2.用哪种文件存储数据
在开始之前还有一个问题是应该使用文本文件还是二进制文件来存储数据?
我们来分析一下两者的优缺点:
文本文件:
- 优点: 能够看懂存放通讯录信息的文件内的内容
- 缺点:信息传输过程繁琐,要取出文件内的信息需要依次取出最小成员信息。
二进制文件:
- 优点: 能够一次取出所需大小的空间,信息传输过程简洁,只需要使用一次
fread
和fwrite
即可 - 缺点: 看不懂文件内的内容,想要知道联系人的具体信息必须启动程序选择展示功能才能看到。
如下:
那么我们应该使用哪种文件来存放信息呢?
这里我选择了二进制文件来存放信息,虽然文本文件能看到通讯录里的相关内容,但是排版也不会像程序运行时显示的那样清晰,所以作用不大,并且使用二进制文件实现更加简单。
2. save_contact
函数设计
save_contact
函数的功能是在程序结束时将此次对通讯录的修改保存到文件中,该函数要实现一下几步。
- 首先我们就需要打开你要存放通讯录信息的文件
这里用
"w"
模式(写模式)打开文件,对上一次的文件进行覆盖,这样就不会出现数据重复的现象
- 将通讯录中的内容一个个写入文件中(使用
fwrite
函数) - 关闭文件(为了刷新文件缓冲区,将数据真正存放到文件中)
如果不明白文件缓冲区是什么的小伙伴可以看博主的上一篇博客:
链接: C语言文件操作
在最后一部分讲到了文件缓冲区的概念和为什么要刷新缓冲区。
这就是save_contact
函数的整体内容啦!现在来看看具体实现代码:
void save_contact(contact* pc)
{
FILE* pf = fopen("contact.txt", "wb");
//判断文件是否成功打开
if (pf)
{
int i = 0;
//将通讯录中的联系人信息逐个写入文件中
for (i = 0; i < pc->sz; ++i)
{
fwrite(pc->people + i,sizeof(per_info),1 , pf);
}
//关闭文件,保存数据
fclose(pf);
//将pf置为空防止野指针的滥用
pf = NULL;
printf("保存通讯录成功!\n");
}
else
{
perror("save_contact::fopen");
exit(errno);
}
}
3. load_contact
函数设计
以同样的方法我们来看看load_contact
的实现思路。
如果是对于简单通讯录实现加载函数则比较简单,就是把文件内的联系人名称依次读取到通讯录即可,但是如果是动态通讯录就稍微复杂一点了。
每次读取了一个联系人之后都要检查此时的联系人数量有没有达到通讯录的最大容量,如果达到,就需要进行增容处理。
上次在设计动态通讯录的时候我们为增容设计了一个函数ins_capcity
,我们现在就可以利用它。
具体代码:
void load_contact(contact** pc)
{
per_info tmp = { 0 };
FILE* pf = fopen("contact.txt", "rb");
int i = 0;
//判断文件是否正常打开
if(pf)
{
//先将信息读取到中间变量tmp中
while (fread(&tmp, sizeof(per_info), 1, pf))
{
//如果达到最大容量,则需增容
if ((*pc)->sz == (*pc)->max_ele)
{
(*pc) = ins_capcity(*pc);
(*pc)->max_ele += INS_ELE;
//printf("增容成功!\n");
}
//将信息放入通讯录中
(*pc)->people[i] = tmp;
++i;
++(*pc)->sz;
}
//关闭文件
fclose(pf);
pf = NULL;
}
else
{
perror("load_contact::fopen");
exit(errno);
}
}
到此,使用文件操作对信息的持久化处理就到此圆满结束了,如果大家有一些不懂的地方或者觉得作者的实现方法还有漏洞的地方可以在评论区提出,作者都会一一回复的哦!如果觉得不错,还请大家点个小小的赞喽哈哈!
5.代码总览
contact.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#define PEOPLE_MAX 100
#define SEX_MAX 5
#define TELE_MAX 12
#define NAME_MAX 20
#define ADDR_MAX 25
#define INIT_CAPCITY 3
#define INS_ELE 2
#include<stdio.h>
#include<string.h>
#include<assert.h>
#include<stdlib.h>
#include<windows.h>
#include<errno.h>
typedef enum option
{
EXIT,
ADD,
DEL,
MODIFY,
SEARCH,
SHOW,
EMPTY
}option;
typedef struct per_info
{
int age;
char sex[SEX_MAX];
char tele[TELE_MAX];
char name[NAME_MAX];
char addr[ADDR_MAX];
}per_info;
/*静态版本*/
//typedef struct contact
//{
// per_info people[PEOPLE_MAX];
// int sz;
//}contact;
/*动态版本*/
typedef struct contact
{
int sz;
int max_ele;
per_info people[];
}contact;
//使用传址调用能够减少空间的使用
//初始化通讯录
void init_contact(contact** pc);
//添加联系人
void add_person(contact** pc);
//显示联系人
void show_person(const contact* pc);
//搜索联系人
void search_person(const contact*pc);
//删除联系人
void del_person(contact** pc);
//修改联系人
void mod_person(contact* pc);
//清空联系人
void empty_person(contact** pc);
//销毁通讯录
void destory_contact(contact* pc);
//保存通讯录
void save_contact(contact* pc);
//加载联系人
void load_contact(contact** pc);
contact.c
#include"contact.h"
/*静态版本*/
//void init_contact(contact* pc)
//{
// //pc不能是空指针
// assert(pc);
// memset(pc->people, 0, sizeof(pc->people));
// pc->sz = 0;
//}
/*动态版本+柔性数组*/
void init_contact(contact** pc)
{
//pc不能是空指针
assert(pc);
contact* ret = (contact*)calloc(1, sizeof(contact) + INIT_CAPCITY * sizeof(per_info));
if (ret)
{
*pc = ret;
(*pc)->max_ele = INIT_CAPCITY;
}
else
{
perror("init_contact::calloc");
}
load_contact(pc);
}
int cmp(const void* a, const void* b)
{
return strcmp(((per_info*)a)->name, ((per_info*)b)->name);
}
/*静态版本*/
//void add_person(contact* pc)
//{
// assert(pc);
// printf("请输入联系人的姓名:>");
// scanf("%s", pc->people[pc->sz].name);
// printf("请输入联系人的性别:>");
// scanf("%s", pc->people[pc->sz].sex);
// printf("请输入联系人的年龄:>");
// scanf("%d", &(pc->people[pc->sz].age));
// printf("请输入联系人的地址:>");
// scanf("%s", pc->people[pc->sz].addr);
// printf("请输入联系人的电话:>");
// scanf("%s", pc->people[pc->sz].tele);
// ++pc->sz;
// qsort(pc->people, pc->sz, sizeof(pc->people[0]), cmp);
// printf("添加成功!\n");
// Sleep(1000);
// system("cls");
//}
contact* ins_capcity(contact* pc)
{
contact* ret = (contact*)realloc(pc, (pc->max_ele + INS_ELE) * sizeof(per_info) + sizeof(contact));
if (ret)
return ret;
else
perror("ins_capcity::realloc");
}
/*动态版本+柔性数组*/
void add_person(contact** pc)
{
assert(pc);
if ((*pc)->sz == (*pc)->max_ele)
{
*pc = ins_capcity(*pc);
(*pc)->max_ele += INS_ELE;
printf("增容成功!\n");
}
printf("请输入联系人的姓名:>");
scanf("%s", (*pc)->people[(*pc)->sz].name);
printf("请输入联系人的性别:>");
scanf("%s", (*pc)->people[(*pc)->sz].sex);
printf("请输入联系人的年龄:>");
scanf("%d", &((*pc)->people[(*pc)->sz].age));
printf("请输入联系人的地址:>");
scanf("%s", (*pc)->people[(*pc)->sz].addr);
printf("请输入联系人的电话:>");
scanf("%s", (*pc)->people[(*pc)->sz].tele);
++(*pc)->sz;
qsort((*pc)->people, (*pc)->sz, sizeof((*pc)->people[0]), cmp);
printf("添加成功!\n");
Sleep(1000);
system("cls");
}
void show_person(const contact* pc)
{
system("cls");
assert(pc);
printf("%-20s\t%-4s\t%-5s\t%-12s\t%-25s\n", "姓名", "年龄", "性别", "电话", "地址");
for (int i = 0; i < pc->sz; ++i)
{
printf("%-20s\t%-4d\t%-5s\t%-12s\t%-25s\n", pc->people[i].name,
pc->people[i].age,
pc->people[i].sex,
pc->people[i].tele,
pc->people[i].addr);
}
printf("\n\n");
}
int find_by_name(const contact* pc, char* name)
{
int left = 0;
int right = pc->sz - 1;
while (left <= right)
{
int mid = (left + right) / 2;
if (strcmp(pc->people[mid].name, name) > 0)
right = mid - 1;
else if (strcmp(pc->people[mid].name, name) < 0)
left = mid + 1;
else
return mid;
}
return -1;
}
void search_person(const contact* pc)
{
assert(pc);
char name[NAME_MAX] = { 0 };
printf("请输入你要查找联系人的姓名:>");
scanf("%s", name);
int pos = find_by_name(pc, name);
if (-1 == pos)
{
printf("找不到该联系人\n");
return;
}
printf("%-20s\t%-4s\t%-5s\t%-12s\t%-25s\n", "姓名", "年龄", "性别", "电话", "地址");
printf("%-20s\t%-4d\t%-5s\t%-12s\t%-25s\n", pc->people[pos].name,
pc->people[pos].age,
pc->people[pos].sex,
pc->people[pos].tele,
pc->people[pos].addr);
}
void del_person(contact** pc)
{
assert(pc);
if ((*pc)->max_ele - (*pc)->sz == INS_ELE)
{
contact* ret = (contact*)realloc(*pc, sizeof(contact) + sizeof(per_info) * ((*pc)->max_ele - INS_ELE));
if (ret)
{
*pc = ret;
(*pc)->max_ele -= 1;
//printf("缩容成功!\n");
}
else
perror("del_person::realloc");
}
printf("请输入你要删除的联系人:>");
char name[NAME_MAX] = { 0 };
scanf("%s", name);
int pos = find_by_name(*pc, name);
if (-1 == pos)
{
printf("你要删除的联系人不存在.\n");
return;
}
for (int i = pos; i < (*pc)->sz - 1; ++i)
{
(*pc)->people[i] = (*pc)->people[i + 1];
}
--(*pc)->sz;
printf("删除成功!\n");
Sleep(1000);
system("cls");
}
void mod_person(contact* pc)
{
printf("请输入你要修改的联系人的名字:>");
char name[NAME_MAX] = { 0 };
scanf("%s", name);
int pos = find_by_name(pc, name);
if (-1 == pos)
{
printf("找不到要修改的联系人。\n");
return;
}
printf("请输入联系人的姓名:>");
scanf("%s", pc->people[pos].name);
printf("请输入联系人的性别:>");
scanf("%s", pc->people[pos].sex);
printf("请输入联系人的年龄:>");
scanf("%d", &(pc->people[pos].age));
printf("请输入联系人的地址:>");
scanf("%s", pc->people[pos].addr);
printf("请输入联系人的电话:>");
scanf("%s", pc->people[pos].tele);
qsort(pc->people, pc->sz, sizeof(pc->people[0]), cmp);
printf("修改成功!\n");
Sleep(1000);
system("cls");
}
/*静态版本*/
//void empty_person(contact* pc)
//{
// assert(pc);
// if (!pc->sz)
// {
// printf("无联系人,无需清空。\n");
// Sleep(1000);
// system("cls");
// return;
// }
// memset(pc, 0, sizeof(contact));
// printf("清空联系人成功!\n");
// Sleep(1000);
// system("cls");
//}
/*动态版本*/
void empty_person(contact** pc)
{
assert(pc);
contact* ret = realloc(*pc, INIT_CAPCITY * sizeof(per_info) + sizeof(contact));
if (ret)
{
*pc = ret;
}
else
perror("empry_person::realloc");
memset(*pc, 0, INIT_CAPCITY * sizeof(per_info) + sizeof(contact));
(*pc)->max_ele = INIT_CAPCITY;
printf("清空联系人成功\n");
Sleep(1000);
system("cls");
}
void destory_contact(contact* pc)
{
pc->sz = 0;
free(pc);
pc = NULL;
}
void save_contact(contact* pc)
{
FILE* pf = fopen("contact.txt", "wb");
if (pf)
{
int i = 0;
for (i = 0; i < pc->sz; ++i)
{
fwrite(pc->people + i,sizeof(per_info),1 , pf);
}
fclose(pf);
pf = NULL;
printf("保存通讯录成功!\n");
}
else
{
perror("save_contact::fopen");
exit(errno);
}
}
void load_contact(contact** pc)
{
per_info tmp = { 0 };
FILE* pf = fopen("contact.txt", "rb");
int i = 0;
if(pf)
{
while (fread(&tmp, sizeof(per_info), 1, pf))
{
if ((*pc)->sz == (*pc)->max_ele)
{
(*pc) = ins_capcity(*pc);
(*pc)->max_ele += INS_ELE;
printf("增容成功!\n");
}
(*pc)->people[i] = tmp;
++i;
++(*pc)->sz;
}
fclose(pf);
pf = NULL;
}
else
{
perror("load_contact::fopen");
exit(errno);
}
}
text.c
#include"contact.h"
void menu(void)
{
printf("****************************\n");
printf("**** 1.add 2.del ****\n");
printf("**** 3.modify 4.search ****\n");
printf("**** 5.show 6.empty ****\n");
printf("**** 0.exit ****\n");
printf("****************************\n");
}
int main()
{
int input = 0;
//创建通讯录
contact* record;
//为使得初始化更灵活,用函数封装
init_contact(&record);
do
{
menu();
printf("请选择你需要做的操作:>");
scanf("%d", &input);
while (getchar() != '\n')
continue;
system("cls");
switch (input)
{
case ADD:
add_person(&record);
break;
case DEL:
del_person(&record);
break;
case MODIFY:
mod_person(record);
break;
case SEARCH:
search_person(record);
break;
case SHOW:
show_person(record);
break;
case EMPTY:
empty_person(&record);
break;
case 0:
save_contact(record);
destory_contact(record);
printf("退出通讯录\n");
break;
default:
printf("输入错误,请重新输入。\n");
break;
}
} while (input);
return 0;
}