【C语言】通讯录《信息写到文件版本》

时间:2023-02-26 22:00:09

????write in front????   

????大家好,我是謓泽,希望你看完之后,能对你有所帮助,不足请指正!共同学习交流????

????2021|2022年度博客之星物联网与嵌入式开发TOP5|TOP4~2021博客之星TOP100~2022博客之星TOP63~作者周榜84﹣作者总榜704~阿里云专家博主 & 阿里云星级博主~掘金优秀创作者⇿InfoQ创作者⇿51CTO红人⇿全网访问量50w+????

????本文由 謓泽 原创 如需转载还请通知⚠

????个人主页-​謓泽51Ctop的博客​????

????欢迎各位→点赞???? + 收藏⭐️ + 留言????

????系列专栏-​謓泽51Ctop的博客_【C语言】从0~1_51CTO博客​????
✉️我们并非登上我们所选择的舞台,演出并非我们所选择的剧本????

 概述 

这个是在之前的动态版本进行添加,如果你不清楚通讯录的静态或者是动态怎么实现。可以看看博主写的前面两篇通讯录的静态和动态的实现↓


增添の功能

相比之前的静态版本还是动态版本。这个信息写到文件版本,无疑是更加完善的。如果你不会文件操作的话,可以看看我前面写过的内存的一篇博客来看看⇣

链接→​【C语言】内存函数_謓泽的博客-CSDN博客​

增添の功能↓

(1)→当通讯录退出的时候,把信息写到文件里面。

(2)→当通讯录初始化的时候,加载文件的信息到通讯录当中去。

在上述的博客当中我们是↓

Destory_Contact(&con);//销毁通讯录【C语言】通讯录《信息写到文件版本》

而在这篇博客我们实现的是首先要保存通讯录,也就是保存信息到文件去。那么我们创建一个函数实现保存信息到文件里。

Save_Contact(&con);//保存信息到文件【C语言】通讯录《信息写到文件版本》


Save_Contact() → 保存通讯录信息

那么首先我们要实现这个保存通讯录信息到文件的这个函数。

Save_Contact() 代码示例如下↓

//读写信息到文件
void Save_Contact(Contact* pc)
{
FILE* pf = fopen("information.txt","w");
//返回值判断
if (pf == NULL)
{
perror("Save_Contact:");
return;
}
//写文件
int i = 0;
for (i = 0; i < pc->sz; i++)
{
//数组名+i就是数组名下标元素的地址。
fwrite(pc->date + i, sizeof(information), 1, pf);
}
//关闭文件
fclose(pf);
pf = NULL;
}

【C语言】通讯录《信息写到文件版本》

那么现在我们就可以试一下,那我们就把张三同学的通讯录信息写下。

张三:?????????????,算了为了张三同学的隐私我们就不这样了(o゚v゚)ノ

直接简单一点打印输入数字①来康康吧。

【C语言】通讯录《信息写到文件版本》

​编辑从上述的截屏当中可以知道我们是写入进去东西的。


读文件操作 

现在我们保存文件是没有任何的问题了,但是当我们再次运行通讯录的时候。通讯录依然会什么都会没有。所以,此时我们就需要进行读文件的一个操作。

我们在初始化的时候就可以进行读文件的一个操作了,因为在此之前已经有增删查改了。

【C语言】通讯录《信息写到文件版本》

那我们封装一个函数用read_file()来进行实现。 

void read_file(Contact* pc)
{
FILE* pf = fopen("information.txt", "r");
//返回值判断
if (pf == NULL)
{
perror("read_file");
return;
}
//读文件
information tmp = {0};//问题:读文件停止(不知道你写入几个信息)?返回值的问题实际count
while (fread(&tmp, sizeof(information), 1, pf))//读不到返回0
{
//是否增容
Cap_inc(pc);
//存放增容元素
pc->date[pc->sz] = tmp;
//加载信息
pc->sz++;
}
//关闭文件
fclose(pf);
pf = NULL;
}

让我们来看看是否能进行一个读文件的操作。检测是非常简单的,我们先输入一个张三,再退出程序。然后,再输入一个李四。看看能不能读写上次张三的信息。

输入界面数字:6 即可(o゚v゚)ノ

第一次的输入! 

【C语言】通讯录《信息写到文件版本》【C语言】通讯录《信息写到文件版本》

上述的读写操作是成功的。 

第二次的输入!

【C语言】通讯录《信息写到文件版本》

从这里我们可以知道它保存了我们上述的读写的值,也就是读文件的操作我们是成功的了。


模块化代码实现 

【C语言】通讯录《信息写到文件版本》

Ⅰ→  test.c

代码示例如下↓

/*
@Note1:当通讯录退出的时候,把信息写道文件。
@Note2:当通讯录初始化的时候,加载文件的信息。
*/
#define _CRT_SECURE_NO_WARNINGS 1
#include"a.h"
#include<Windows.h>
#pragma warning(disable:6031)
//颜色函数
void color(short x)
{
if (x >= 0 && x <= 15)
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), x);
else
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 7);
}
//创建菜单函数
void menu()
{
color(0);//黑色
system("cls");//清屏

color(12);//白色
printf("¤--------------------------------------------¤\n");
printf("|■■■■■■■■→通讯录v1.0←■■■■■■■■|\n");
printf("|———————————————————————|\n");
printf("|★★★★★→1.increase ■ 2.deLete←★★★★★|\n");
printf("|★★★★★→3.find ■ 4.Revise←★★★★★|\n");
printf("|★★★★★→5.Check ■ 6.Print ←★★★★★|\n");
printf("|★★★★★→0.Exit ■ ←★★★★★|\n");
printf("|———————————————————————|\n");
printf("|■■■■■■■■→通讯录v1.0←■■■■■■■■|\n");
printf("¤--------------------------------------------¤\n");
}
enum Number
{
Exit,
Increase,
DeLete,
Find,
Revise,
Check,
Print,
};
int main(void)
{
menu();
int input = 0;
//当然初始化全0:Contact con = {0};也是可以的,当然我们这里的初始化不是这样的原因是可以应对比较复杂的问题。
Contact con;//初始化通讯录
//给date申请一块连续的内存空间存放在堆区上。
InitContact(&con);
do
{
color(5);
printf("¤----------------¤\n");
printf("|请输入界面上的数字|:");
scanf("%d", &input);
printf("¤----------------¤\n");
color(1);
switch (input)
{
case Exit:
//销毁之前先保存。
Save_Contact(&con);
//把通讯录里面的内存空间date给释放掉。
Destory_Contact(&con);//销毁通讯录。
printf("══════════════@\n");
printf("退出通讯录v1.0@\n");
printf("══════════════@\n");
break;
case Increase:
//增加人的信息,放在通讯录当中去。
Add_Contact(&con);
break;
case DeLete:
//删除通讯人的信息
DeLete_Contact(&con);
break;
case Find:
//查找指定通讯录人的信息
Find_Contact(&con);
break;
case Revise:
//修改指定通讯录人的信息
Revise_Contact(&con);
break;
case Check:
//排查通讯录当中人员的信息
Check_Contact(&con);
break;
case Print:
//打印通讯录当中人员的信息。
Print_Contact(&con);
break;
default:printf("你输入的数字找不到,请重新输入~\n");
}
} while (input);
return 0;
}

Ⅱ→address_book.h

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<Windows.h>
#include<string.h>

#define Max_name 5
#define Max_age 100
#define Max_genger 3
#define Max_address 20
#define Max_telephone 20

//#define num 1000 //静态的存放
#define Defsz 3 //一开始的值
#define Inc 2 //每次的增量

//类型的定义
typedef struct information
{
//名字、年龄、性别、电话、地址。
char name[Max_name];
char age[Max_age];
char genger[Max_genger];
char telephone[Max_telephone];
char address[Max_address];
}information;

//通讯录 - 动态版本
typedef struct Contact
{
//这里用了个结构体嵌套
information* date;//指向动态内存的申请空间,用于存放联系人的信息。
int sz;//记录当前通讯录有效信息的个数。
int capacity;//表示当前通讯录的最大"容量"大小。
}Contact;

//初始化通讯录
void InitContact(Contact* pc);
//增加通讯录信息
void Add_Contact(Contact* pc);
//打印通讯录的信息
void Print_Contact(const Contact* pc);
//删除通讯人的信息
void DeLete_Contact(Contact* pc);
//查找指定通讯录人的信息
void Find_Contact(Contact* pc);
//修改指定通讯录人的信息
void Revise_Contact(Contact* pc);
//排查通讯录当中人员的信息
void Check_Contact(Contact* pc);
//销毁通讯录
void Destory_Contact(Contact* pc);

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

//读文件-加载文件
void read_file(Contact* pc);

//检测增容
void Cap_inc(Contact* pc);

Ⅲ→address_book.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"address_book.h"

//读文件-加载文件
void read_file(Contact* pc)
{
FILE* pf = fopen("information.txt", "r");
//返回值判断
if (pf == NULL)
{
perror("read_file");
return;
}
//读文件
information tmp = {0};//问题:读文件停止(不知道你写入几个信息)?返回值的问题实际count
while (fread(&tmp, sizeof(information), 1, pf))//读不到返回0
{
//是否增容
Cap_inc(pc);
//存放增容元素
pc->date[pc->sz] = tmp;
//加载信息
pc->sz++;
}
//关闭文件
fclose(pf);
pf = NULL;
}

//动态版本进行初始化
void InitContact(Contact* pc)
{
pc->date = (information*)malloc(Defsz * sizeof(information));
if (pc->date == NULL)
{
perror("InitContact");
return;
}
//如果返回的为空指针的情况
pc->sz = 0;//sz初始化
pc->capacity = Defsz;//当前最大"容量"

//读文件-加载文件
read_file(pc);
}

//增容函数
void Cap_inc(Contact* pc)
{
//当记录当前通讯录有效信息的个数 等于 表示当前通讯录的最大"容量"大小。
if (pc->sz == pc->capacity)
{
//给堆区增加"容量",因为空间不够。
information* pa = (information*)realloc(pc->date, (pc->capacity + Inc)*(sizeof(information)));
if (pa != NULL)
{
pc->date = pa;//把这块空间移交到date进行维护
pc->capacity = pc->capacity + Inc;//由于这里我们进行了增容也需要把容量改变成+Inc的值

printf("增加联系人已成功(@^0^)\n");
}
//增容失败
else
{
perror("Add_Contact:");
printf("增加联系人失败!(ToT)/~~~\n");
return;
}
}
}

//动态版本增加
void Add_Contact(Contact* pc)
{
//检测容量
Cap_inc(pc);
//增加一个人的信息
printf("\n");
printf("请输入增加人的名字->:");
scanf("%s", pc->date[pc->sz].name);//注意→数组名是首元素地址,所以不用进行取地址.

printf("请输入增加人的年龄->:");
scanf("%s", pc->date[pc->sz].age);

printf("请输入增加人的性别->:");
scanf("%s", pc->date[pc->sz].genger);

printf("请输入增加人的电话->:");
scanf("%s", pc->date[pc->sz].telephone);

printf("请输入增加人的地址->:");
scanf("%s", pc->date[pc->sz].address);
//成功~
pc->sz++;
printf("★恭喜你~添加信息成功★\n");
printf("\n");
}

//读写信息到文件
void Save_Contact(Contact* pc)
{
FILE* pf = fopen("information.txt","w");
//返回值判断
if (pf == NULL)
{
perror("Save_Contact:");
return;
}
//写文件
int i = 0;
for (i = 0; i < pc->sz; i++)
{
//数组名+i就是数组名下标元素的地址。
fwrite(pc->date + i, sizeof(information), 1, pf);
}
//关闭文件
fclose(pf);
pf = NULL;
}

void Print_Contact(const Contact* pc)
{
int i = 0;
//打印标题栏的信息
printf("|-------------------------------------------------------|\n");
printf("|%-3s\t%-3s\t%-3s\t%-11s\t%-10s\t|\n", "名字", "年龄", "性别", "电话", "地址");

for (i = 0; i < pc->sz; i++)
{
printf("|%-3s\t%-3s\t%-3s\t%-11s\t%-10s\t|\n", pc->date[i].name,
pc->date[i].age,
pc->date[i].genger,
pc->date[i].telephone,
pc->date[i].address);
}
}

static int FindByname(Contact* pc, char name[])
{
int i = 0;
//用for循环进行遍历
for (i = 0; i < pc->sz; i++)
{
//strcmp()比较字符串
if (strcmp(pc->date[i].name, name) == 0)
{
return i;//返回下标
}
}
return -1;
}

void DeLete_Contact(Contact* pc)
{
char name[Max_name] = { 0 };
//通讯录为空的时候
if (pc->sz == 0)
{
printf("通讯录当中没有任何信息\n");
return;
}
printf("请输入你要删除的名字->:");
scanf("%s", name);
//查找要删除的人:有/没有
int ret = FindByname(pc, name);
if (ret == -1)
{
printf("没有查找到当前人的信息\n");
return;
}
//删除
int i = 0;
for (i = ret; i < pc->sz - 1; i++)
{
pc->date[i] = pc->date[i + 1];
}
pc->sz--;//因为我们删除成功下标要减1
printf("★恭喜你~删除信息成功★\n");
printf("\n");
}

void Find_Contact(Contact* pc)
{
char name[Max_name] = { 0 };
printf("请输入你要查找的名字->:");
scanf("%s", name);
int ret = FindByname(pc, name);
if (ret == -1)
{
printf("没有查找到当前人的名字\n");
return;
}
else
{
printf("|-------------------------------------------------------|\n");
printf("|%-3s\t%-3s\t%-3s\t%-11s\t%-10s\t|\n", "名字", "年龄", "性别", "电话", "地址");

printf("|%-3s\t%-3s\t%-3s\t%-11s\t%-10s\t|\n",
pc->date[ret].name,
pc->date[ret].age,
pc->date[ret].genger,
pc->date[ret].telephone,
pc->date[ret].address);
printf("查找%sの信息成功~\n", pc->date[ret].name);
}
}

void Revise_Contact(Contact* pc)
{
char name[Max_name] = { 0 };
printf("请输入你要修改通讯录人的名字->:");
scanf("%s", name);
int ret = FindByname(pc, name);
if (ret == -1)
{
printf("当前修改人的信息不存在\n");
return;
}
else
{
printf("请输入修改人的名字->:");
scanf("%s", pc->date[ret].name);//注意→数组名是首元素地址,所以不用进行取地址.

printf("请输入修改人的年龄->:");
scanf("%s", pc->date[ret].age);

printf("请输入修改人的性别->:");
scanf("%s", pc->date[ret].genger);

printf("请输入修改人的电话->:");
scanf("%s", pc->date[ret].telephone);

printf("请输入修改人的地址->:");
scanf("%s", pc->date[ret].address);
printf("恭喜你,修改成功~\n");
}
}

int sort_name_max(const void* e1, const void* e2)
{
return (strcmp(((struct Contact*)e1)->date->name, ((struct Contact*)e2)->date->name));
}

void Check_Contact(Contact* pc)
{
//qosrt()函数首字母进行排序
qsort(pc->date, pc->sz, sizeof(pc->date[0]), sort_name_max);
printf("|-------------------------------------------------------|\n");
printf("|%-3s\t%-3s\t%-3s\t%-11s\t%-10s\t|\n", "名字", "年龄", "性别", "电话", "地址");
int i = 0;
for (i = 0; i < pc->sz; i++)
{
printf("|%-3s\t%-3s\t%-3s\t%-11s\t%-10s\t|\n", pc->date[i].name,
pc->date[i].age,
pc->date[i].genger,
pc->date[i].telephone,
pc->date[i].address);
}
}

void Destory_Contact(Contact* pc)
{
//销毁通讯录实际上就是把date给释放掉,free()函数进行回收即可。
free(pc -> date);
pc->date = NULL;
pc->sz = 0;
pc->capacity = 0;
//记得清0☆⌒(*^-゜)v THX!!
}

✨最后✨

那么这个就已经实现了通讯录的最终版本了,这个项目适合已经把C语言学完的人做。巩固自己以往的能力,做个这个提升自己逻辑思维和代码能力的一个小的项目。

【C语言】通讯录《信息写到文件版本》