一、实现思路:
通过在项目工作路径创建一个文件用来存放退出通讯录前存放的信息,当下一次打开通讯录时通过该文件将其加载到通讯录中,实现通讯录信息的保存。主要用到的是fread和fwrite这两个函数来实行对文件的保存和读取,这两个文件的读写用的是二进制读写方式,在使用结构体实现通讯录(静态版本+动态版本)-CSDN博客(更详细的实现逻辑可参考)的基础上进行了略微修改+拓展。复现该代码时需要提前在创建一个文件如contact.txt,可参考下图:
如需创建在其他目录下,则需要进行修改fopen这个函数的路径参数。
二、fopen函数参数及功能介绍
在编写程序的时候,在打开文件的同时,都会返回一个FILE*的指针变量指向该文件,也相当于建立了指针和文件的关系。filename可用常量字符串来表示文件的路径,路径包括相对路劲和绝对路径,上面所示在同一目录就是相对路径,
绝对路径示例:
mode表示文件的打开模式,下面是文件的打开模式:
代码示例:
#include <stdio.h>
int main ()
{
FILE * pFile;
//打开⽂件
pFile = fopen ("myfile.txt","w");
//⽂件操作
if (pFile!=NULL)
{
fputs ("fopen example",pFile);
//关闭⽂件
fclose (pFile);
}
return 0;
}
三、fread函数参数及功能介绍:
该函数针对的是二进制文件进行读取(可以理解为将文件的数据读取到代码程序里的某个变量之类的)。ptr表示从该文件读取的信息放到哪里(将其放到ptr所指向的空间),size表示该类型占有多少字节,count表示要从该文件里读取该类型的个数,stream表示文件指针(每次进行文件读写的时候,这个指针会自动进行往后调整指向,不需要程序员自己进行调整,文件指针是跟文件一一对应的,是用来维护文件的,文件指针保存了一个地址,该地址指向的空间保存了很多文件信息,具体程序员不需要知道)。总的来说该函数的功能就是从文件中读取count*size个字节大小的空间到ptr所指向的空间里
四、fwrite函数的参数及功能介绍:
该函数也是对二进制文件的进行操作,不过是将程序中的信息写入文件,参数和fread函数看起来一致,ptr表示将ptr所指向的空间的数据信息放入到文件中,size表示该类型占有多少字节,count表示要从该文件里读取该类型的个数,stream表示文件指针。该函数的功能是将ptr所指向的count*size写到文件里。
五、基于文件读写进行改进的通讯录
针对通讯录fwrite可算作是保存信息,fwrite是用来读取文件所放的信息的。
代码和以及逻辑上篇文章(使用结构体实现通讯录(静态版本+动态版本)-CSDN博客)基本一致可供参考。只是对一部分进行了修改
文件读写代码如下:
test.c文件代码如下:(在打开通讯录时进行加载文件所存储的通讯录中联系人的信息,在退出通讯录将数据进行保存,以次来实现数据的存储和保存)
#include"contact.h"
int main()
{
int input = 0;
struct contact con;
//对这个con结构体进行初始化,否则可能会出现非法访问内存的问题
init_contact(&con);
do
{
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case 1:
add_contact(&con);//要对结构体内容进行改变,必须要传址
break;
case 2:
del_contact(&con);//要对结构体内容进行改变,必须要传址
break;
case 3:
search(&con);
break;
case 4:
modify(&con);
break;
case 5:
show_contact(&con);
break;
case 6:
sort(&con);
break;
case 0:
save_contact(&con);
release_space(&con);//退出释放开辟的空间
printf("退出通讯录,保存成功\n");
break;
default:
printf("输入错误,请重新输入\n");
break;
}
} while (input);
return 0;
}
contact.h文件代码如下:
#pragma once
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<assert.h>
#define init_cmp 1//默认最初的容量为1
#define max_name 20//存储联系人电话最大的字符数为max_name-1,这是由于最后一位会有\0字符
#define max_sex 5//存储联系人电话最大的字符数为max_sex-1,这是由于最后一位会有\0字符
#define max_tel 12//存储联系人电话最大的字符数为max_tel-1,这是由于最后一位会有\0字符
#define add_cam_num 2//每次增容数量
//考虑到通讯录要存储联系人的姓名,年龄,性别,电话,所以考虑使用结构体来存储数据
struct People_info
{
char name[max_name];
int age;
char sex[max_sex];
char tel[max_tel];
};
struct contact
{
struct People_info *people;
int sz;//用来记录当前存了多少联系人了
int cap;//记录当前通讯录最大的空间存储
};
//菜单打印函数
void menu();
//初始化通讯录
void init_contact(struct contact *p);
//增加通讯录联系人
void add_contact(struct contact *p);
//显示通讯录
void show_contact(struct contact*p);
//删除通讯录中的联系人
void del_contact(struct contact* p);
//查找通讯录中的联系人
void search(struct contact* p);
//修改通讯录中的联系人
void modify(struct contact* p);
//根据名字进行升序排序
void sort(struct contact* p);
//加载以前存储在文件里的信息
void load_contact(struct contact* p);
//退出保存通讯录信息
void save_contact(struct contact* p);
//销毁通讯录
void release_space(struct contact* p);
contact.c文件代码如下:
#define _CRT_SECURE_NO_WARNINGS 1
#include"contact.h"
void menu()
{
printf("****** 1.add 2.del *****\n");
printf("****** 3.search 4.modify *****\n");
printf("****** 5.show 6.sort *****\n");
printf("****** 0.exit *****\n");
}
void modify_menu()
{
printf("****** 1.name 2.age *****\n");
printf("****** 3.sex 4.tel *****\n");
printf("****** 0.no modify *****\n");
}
//搜寻查找
//检查能否增容,如果能每次将会增加2个容量,增容成功返回1,失败返回0
int check_cam(struct contact* p)
{
if ((p->sz) >= (p->cap))
{
p->cap += add_cam_num;
struct People_info* ptr = (struct People_info*)realloc(p->people, sizeof(struct People_info) * (p->cap));
if (ptr == NULL)
{
printf("增容失败\n");
return 0;
}
p->people = ptr;
printf("增容成功\n");
return 1;
}
return 1;
}
//保存
void save_contact(struct contact* p)
{
//打开文件
FILE* pf = fopen("contact.txt", "wb");
if (pf == NULL)
{
perror("save_contact--fopen");
return;
}
int i = 0;
for (i = 0; i < p->sz; i++)
{
fwrite(p->people+i, sizeof(struct People_info), 1, pf);
}
//关闭文件
fclose(pf);
pf = NULL;
}
int find(char*str,struct contact*p)
{
for (int i = 0; i < p->sz; i++)
{
if (!strcmp(str, p->people[i].name))
{
return i;
}
}
return -1;
}
//加载以前存储在文件里的信息
void load_contact(struct contact* p)
{
//打开文件
FILE* pf = fopen("contact.txt", "rb");
if (pf == NULL)
{
perror("load_contact--fopen");
return;
}
struct People_info ptr = { 0 };
//fread函数返回成功读取的元素总数,每次读取一个就返回1,当没读到数据时就会返回0
while (fread(&ptr, sizeof(struct People_info), 1, pf))//每次读取一个数据,没读取到就退出
{
//检查容量
if (check_cam(p))
{
p->people[p->sz] = ptr;
p->sz++;
}
else
{
printf("容量不够,无法加载以前的数据\n");
return;
}
}
//关闭文件
fclose(pf);
//将文件指针置为空指针
pf = NULL;
printf("通讯录中的信息加载成功\n");
}
//对通讯录进行初始化
void init_contact(struct contact* p)
{
p->people = (struct People_info*)malloc(sizeof(struct People_info) * init_cmp);
if (p->people==NULL)
{
perror("malloc:");
return;
}
p->sz = 0;
p->cap = init_cmp;//防止空间浪费,设置初始容量大小为1
load_contact(p);
}
//增加通讯录联系人
void add_contact(struct contact* p)
{
if (check_cam(p))
{
printf("请输入要添加联系人的姓名:>");
scanf("%s", p->people[p->sz].name);
printf("\n");
printf("请输入要添加联系人的年龄:>");
scanf("%d", &(p->people[p->sz].age));
printf("\n");
printf("请输入要添加联系人的性别:>");
scanf("%s", p->people[p->sz].sex);
printf("\n");
printf("请输入要添加联系人的电话:>");
scanf("%s", p->people[p->sz].tel);
p->sz++;
printf("添加成功\n");
}
else
{
printf("容量不够无法添加\n");
return;
}
}
//通讯录中的显示
void show_contact(struct contact* p)
{
printf("%-10s\t%-5s\t%-5s\t%-20s\n", "姓名", "年龄", "性别", "电话");
for (int i = 0; i < p->sz; i++)
{
printf("%-10s\t%-5d\t%-5s\t%-20s\n",
p->people[i].name,
p->people[i].age,
p->people[i].sex,
p->people[i].tel);
}
printf("\n");
}
//删除通讯录中的联系人
void del_contact(struct contact* p)
{
if (!p->sz)
{
printf("通讯录为空,无法进行删除操作\n");
return;
}
printf("请输入要删除的联系人:>");
char str[max_name]="\0";
scanf("%s", str);
int del=find(str,p);
if (del == -1)//通讯录找不到该联系人就返回,提前结束
{
printf("要删除的人不存在\n");
return;
}
//删除联系人
for (int i = del; i < p->sz - 1; i++)
{
p->people[i].age = p->people[i + 1].age;
strcpy(p->people[i].name, p->people[i + 1].name);
strcpy(p->people[i].sex, p->people[i + 1].sex);
strcpy(p->people[i].tel, p->people[i + 1].tel);
}
p->sz--;
printf("删除成功\n");
}
//查找通讯录中的联系人
void search(struct contact* p)
{
printf("请输入想要查找的联系人姓名:>");
char str[max_name] = "\0";
scanf("%s", str);
int ret=find(str, p);
if (ret == -1)
{
printf("在该通讯录中找不到\n");
return;
}
printf("%-10s\t%-5s\t%-5s\t%-20s\n", "姓名", "年龄", "性别", "电话");;
printf("%-10s\t%-5d\t%-5s\t%-20s\n",
p->people[ret].name,
p->people[ret].age,
p->people[ret].sex,
p->people[ret].tel);
}
//修改通讯录中的联系人
void modify(struct contact* p)
{
printf("请输入要修改人的名字:>");
char str[max_name] = "\0";
scanf("%s", str);
int ret = find(str, p);
if (ret == -1)
{
printf("要修改的人在通讯录中不存在\n");
}
else
{
int input = 0;
do
{
modify_menu();
printf("请选择想要修改的:>");
scanf("%d", &input);
char tmp[max_name] = "\0";//用max_name比其他的大,所以足够容下其他的修改输入
int tt = 0;
switch (input)
{
case 1:
printf("请输入想要修改联系人的名字:>");
scanf("%s", tmp);
strcpy(p->people[ret].name, tmp);
break;
case 2:
printf("请输入想要修改联系人的年龄:>");
scanf("%d", &tt);
p->people[ret].age = tt;
break;
case 3:
printf("请输入想要修改联系人的性别:>");
scanf("%s", tmp);
strcpy(p->people[ret].sex, tmp);
break;
case 4:
printf("请输入想要修改联系人的电话:>");
scanf("%s", tmp);
strcpy(p->people[ret].tel, tmp);
break;
case 0:
printf("不修改\n");
break;
default :
printf("选择错误请重新选择\n");
break;
}
} while (input);
}
}
//排序,按照名字字符进行排序
int cmp(const void* str1, const void* str2)
{
return strcmp(((struct People_info*)str1)->name, ((struct People_info*)str2)->name);
}
void sort(struct contact* p)
{
int num = p -> sz;
struct contact* ptr = p;
qsort(ptr->people, p->sz, sizeof(p->people[0]), cmp);
show_contact(p);
}
void release_space(struct contact* p)
{
free(p->people);
p->people = NULL;
p->cap = 0;
p->sz = 0;
}
测试结果(主要对文件的读取和保存进行测试,其他的测试可参考
①通过向通讯录增加3个人的信息,然后退出
②第二次打开通讯录可直接看到上次输入的信息
总结:本文主要介绍了fopen,fread和fwrite参数以及功能,并在这个基础上完成了对通讯录的改造(以实现信息的存储功能)。感谢大家浏览,如有不足错误之处望大家批评指正!!!