作者:小树苗渴望变成参天大树
作者宣言:认真写好每一篇博客
作者gitee:gitee
如 果 你 喜 欢 作 者 的 文 章 ,就 给 作 者 点 点 关 注 吧!
通讯录
前言
我们前面学习了关于结构体,动态内存管理,文件操作这些知识点之后,我们可以自己来设计一个小项目,将我们学到的知识串联起来,才能更好加深我们对知识的理解,我们将使用结构体来存放用户信息,用结构体的自引用来存放数据,在实现一个动态增长的功能,最后在将我们存放的数据通过文件操作的知识将他们存放在一起,不会以至于下次运行时前面的数据没有了我们接下来进入正文,手把手带你实现每个功能!!!
一、前期准备
我们在之前三子棋小游戏的时候说过,我们实现这种小项目前,需要将函数的声明、定义和测试分开来写,所以我们需要建立三个文件,这样的目的是将项目变成清晰化。
二、头文件(contact.h)
我们需要用一个结构体来存放联系人的信息,在用另一个结构体存放数据,使用到的是结构体的嵌套定义,将其放在头文件里面
typedef struct PeoInfo
{
char name[MAX_NAME];
int age;
char sex[MAX_SEX];
char tele[MAX_TELE];
}PeoInfo;
typedef struct Contact
{
PeoInfo* data;//用来存放每个联系人的各个信息
int sz;//记录通讯录有多少人的
int capicity;//当前通讯录最大容量
}Contact;
在存放联系人信息的结构体中,每个数组的大小我用宏定义了一个常量,方便以后的修改
#define MAX_NAME 20
#define MAX_SEX 10
#define MAX_TELE 12
#define Init_Max 3
#define Add_Max 2
强调一点这不是结构体的自引用,而是嵌套定义
我们需要在还需要把函数的声明放到这个头文件里面,所以头文件里面完整的代码为
#define MAX_NAME 20
#define MAX_SEX 10
#define MAX_TELE 12
#define Init_Max 3//初始化容量
#define Add_Max 2//每次增容量
#include<stdio.h>
#include<assert.h>
#include<string.h>
#include<stdlib.h>
typedef struct PeoInfo
{
char name[MAX_NAME];
int age;
char sex[MAX_SEX];
char tele[MAX_TELE];
}PeoInfo;
typedef struct Contact
{
PeoInfo* data;
int sz;//记录通讯录有多少人的
int capicity;//当前通讯录最大容量
}Contact;
void InitContact(Contact* ps);//初始化通讯录
void AddContact(Contact* ps);//增加联系人的信息
void ShowContact(Contact* ps);//显示所有联系人
void SearchContact(Contact* ps);//查找联系人
void ModifContact(Contact* ps);//修改联系人的信息
void DelContact(Contact* ps);//删除联系人的信息
void SortContact(Contact* ps);//排序
void EmptyContact(Contact* ps);//清空
void SaveContact(Contact* ps);//保存文件
void ReadContact(Contact* ps);//读取文件
void DestroyContact(Contact* ps);//销毁
接下来我将依次实现这些功能
三、测试文件(test.c)
我们将主函数写在这个文件里面,方便测试代码
#include"contact.h"
void menu()
{
printf("*****************************************\n");
printf("***** 1.添加 2.显示 ******\n");
printf("***** 3.查找 4.修改 ******\n");
printf("***** 5.删除 6.排序 ******\n");
printf("***** 7.清空 0.退出 ******\n");
printf("*****************************************\n");
}
int main()
{
int input = 0;
Contact con = {
0};
InitContact(&con);
do
{
menu();
printf("请输入你要操作的选项:");
scanf("%d", &input);
switch (input)
{
case 1:
AddContact(&con);
break;
case 2:
ShowContact(&con);
break;
case 3:
SearchContact(&con);
break;
case 4:
ModifContact(&con);
break;
case 5:
DelContact(&con);
break;
case 6:
SortContact(&con);
break;
case 7:
EmptyContact(&con);
break;
case 0:
SaveContact(&con);
DestroyContact(&con);//销毁
printf("退出通讯录系统\n");
break;
default:
printf("输入错误,请重新输入\n");
break;
}
} while (input);
return 0;
}
相信看懂我写的三子棋小游戏博客,对这个应该理解起来难度不大
四、定义文件(contact.c)
4.1初始化
void InitContact(Contact* ps)
{
ps->data = (int*)malloc(sizeof(PeoInfo) * Init_Max);
if (ps->data == NULL)
{
perror("malloc:");
return;
}
ps->sz = 0;
ps->capicity = Init_Max;
ReadContact(ps);
}
4.2是否增容
void CheakConTact(Contact* ps)
{
if (ps->sz == ps->capicity)
{
int* ptr = (int*)realloc(ps->data, sizeof(PeoInfo) * (ps->capicity + Add_Max));
if (ptr == NULL)
{
perror("realloc:");
return;
}
else
{
ps->data = ptr;
}
printf("增容成功!\n");
ps->capicity += Add_Max;
}
}
4.3添加
void AddContact(Contact* ps)//增加联系人的信息
{
assert(ps);
CheakConTact(ps);//先检查是否需要增容
printf("请输入联系人的姓名:");
scanf("%s", ps->data[ps->sz].name);
printf("请输入联系人的年龄:");
scanf("%d", &(ps->data[ps->sz].age));
printf("请输入联系人的性别:");
scanf("%s", ps->data[ps->sz].sex);
printf("请输入联系人的电话:");
scanf("%s", ps->data[ps->sz].tele);
ps->sz++;
printf("添加成功!\n");
}
4.4显示
void ShowContact(Contact* ps)//显示所有联系人
{
if (ps->sz == 0)
{
printf("通讯录为空!\n");
return;
}
int i = 0;
printf("%-5s\t%-4s\t%-4s\t%-15s\n", "姓名", "年龄", "性别", "电话");//水平制表符,左对齐
for (i = 0; i < ps->sz; i++)
{
printf("%-5s\t%-4d\t%-4s\t%-15s\n", ps->data[i].name, ps->data[i].age, ps->data[i].sex, ps->data[i].tele);
}
}
4.5判断有没有此联系人
因为不管是删除或者修改,都涉及到找到那个指定的联系人,所以包装成一个函数:
int PanDuan(Contact* ps)//判断有没有此联系人
{
if (ps->sz == 0)
{
printf("通讯录为空!\n");
return 0;
}
char name[MAX_NAME];
printf("请输入你要查找的联系人的姓名:");
scanf("%s", name);
int i = 0;
for (i = 0; i < ps->sz; i++)
{
if (strcmp(ps->data[i].name, name) == 0)
{
break;
}
}
return i;
4.6查找
void SearchContact(Contact* ps)//查找联系人
{
int pos = PanDuan(ps);
if (pos == 0)
{
return;
}
if (pos == ps->sz)
{
printf("没有你要查找的联系人!\n");
return;
}
printf("查找成功!,此人的信息如下:\n");
printf("%-5s\t%-4s\t%-4s\t%-15s\n", "姓名", "年龄", "性别", "电话");
printf("%-5s\t%-4d\t%-4s\t%-15s\n", ps->data[pos].name, ps->data[pos].age, ps->data[pos].sex, ps->data[pos].tele);
}
4.7修改
void ModifContact(Contact* ps)//修改联系人的信息
{
int pos = PanDuan(ps);
if (pos == 0)
{
return;
}
if (pos == ps->sz)
{
printf("没有你要修改的联系人!\n");
return;
}
printf("查找成功,请修改他的信息:\n");
printf("请输入联系人的姓名:");
scanf("%s", ps->data[pos].name);
printf("请输入联系人的年龄:");
scanf("%d", &(ps->data[pos].age));
printf("请输入联系人的性别:");
scanf("%s", ps->data[pos].sex);
printf("请输入联系人的电话:");
scanf("%s", ps->data[pos ].tele);
printf("修改成功!\n");
}
4.8删除
void DelContact(Contact* ps)//删除联系人的信息
{
int pos = PanDuan(ps);
if (pos == 0)
{
return;
}
if (pos == ps->sz)
{
printf("没有你要删除的联系人信息!\n");
return;
}
int j = pos;
for (j = pos; j < ps->sz - 1; j++)
{
ps->data[j] = ps->data[j + 1];
}
ps->sz--;
printf("删除成功!\n");
}
4.8排序(按名字)
int cmp_name(const void* e1, const void* e2)
{
return strcmp(((PeoInfo*)e1)->name, ((PeoInfo*)e2)->name);
}
void SortContact(Contact* ps)//排序
{
qsort(ps->data, ps->sz,sizeof(PeoInfo), cmp_name);
printf("排序成功!\n");
}
4.9清空
void EmptyContact(Contact* ps)//清空
{
ps->sz = 0;
printf("清空成功!\n");
}
4.10保存文件和读取文件
第一种:
保存文件
void SaveContact(Contact* ps)
{
assert(ps);
FILE* pf = fopen("tongxunlu.txt", "w");
if (NULL == pf)
{
perror("fopen:");
return;
}
int i = 0;
for (i = 0; i < ps->sz; i++)
{
fprintf(pf, "%s %d %s %s\n", ps->data[i].name, ps->data[i].age, ps->data[i].sex, ps->data[i].tele);
}
fclose(pf);
pf = NULL;
printf("保存成功\n");
}
读取文件:
void ReadContact(Contact* ps)//读取文件
{
assert(ps);
FILE* pf = fopen("tongxunlu.txt", "r");
if (pf == NULL)
{
perror("fopen:");
return;
}
//读取文件
PeoInfo tmp = {
0 };
char c = 0;
int r_n = 0;
while (!feof(pf))
{
c = fgetc(pf);
if (c == '\n')
{
r_n++;
}
}
rewind(pf);
for (ps->sz = 0; ps->sz < r_n; ps->sz++)
{
CheakConTact(ps);//先检查是否需要增容
fscanf(pf, "%s %d %s %s", ps->data[ps->sz].name, &(ps->data[ps->sz].age), ps->data[ps->sz].sex, ps->data[ps->sz].tele);
}
printf("读取文件成功!\n");
fclose(pf);
pf = NULL;
}
第二种:
保存文件
void SavehjContact(struct Contact* ps)
{
FILE* pfWrite = fopen("contact.dat", "wb");
if (pfWrite == NULL)
{
printf("SaveContact:%s\n", strerror(errno));
return;
}
//写文件
int i = 0;
for (i = 0; i < ps->size; i++)
{
fwrite(&(ps->date[i]),sizeof(struct PeoInfo),1,pfWrite);//以二进制读入文件
}
//关闭文件
fclose(pfWrite);
pfWrite = NULL;
}
读取文件
void ReadContact(struct Contact* ps)
{
struct PeoInfo tmp = {
0 };
FILE* pfRead = fopen("contact.dat", "rb");
if (pfRead == NULL)
{
printf("LoadContact:%s\n", strerror(errno));
return;
}
//读文件
while (fread(&tmp, sizeof(struct PeoInfo),1, pfRead))//一次读取一个,返回实际读取的个数
{
CheckContact(ps);
ps->date[ps->size] = tmp;
ps->size++;
}
//关闭文件
fclose(pfRead);
pfRead = NULL;
}
最后要将读取文件放在初始化函数中去
4.11销毁
void DestroyContact(Contact* ps)//销毁
{
assert(ps);
free(ps->data);
ps->data = NULL;
ps->sz = 0;
ps->capicity = 0;
}
4.12定义文件完整代码
#include"contact.h"
void InitContact(Contact* ps)
{
ps->data = (int*)malloc(sizeof(PeoInfo) * Init_Max);
if (ps->data == NULL)
{
perror("malloc:");
return;
}
ps->sz = 0;
ps->capicity = Init_Max;
ReadContact(ps);
}
void CheakConTact(Contact* ps)
{
if (ps->sz == ps->capicity)
{
int* ptr = (int*)realloc(ps->data, sizeof(PeoInfo) * (ps->capicity + Add_Max));
if (ptr == NULL)
{
perror("realloc:");
return;
}
else
{
ps->data = ptr;
}
printf("增容成功!\n");
ps->capicity += Add_Max;
}
}
void AddContact(Contact* ps)//增加联系人的信息
{
assert(ps);
CheakConTact(ps);
printf("请输入联系人的姓名:");
scanf("%s", ps->data[ps->sz].name);
printf("请输入联系人的年龄:");
scanf("%d", &(ps->data[ps->sz].age));
printf("请输入联系人的性别:");
scanf("%s", ps->data[ps->sz].sex);
printf("请输入联系人的电话:");
scanf("%s", ps->data[ps->sz].tele);
ps->sz++;
printf("添加成功!\n");
}
void ShowContact(Contact* ps)//显示所有联系人
{
if (ps->sz == 0)
{
printf("通讯录为空!\n");
return;
}
int i = 0;
printf("%-5s\t%-4s\t%-4s\t%-15s\n", "姓名", "年龄", "性别", "电话");
for (i = 0; i < ps->sz; i++)
{
printf("%-5s\t%-4d\t%-4s\t%-15s\n", ps->data[i].name, ps->data[i].age, ps->data[i].sex, ps->data[i].tele);
}
}
int PanDuan(Contact* ps)//判断有没有此联系人
{
if (ps->sz == 0)
{
printf("通讯录为空!\n");
return 0;
}
char name[MAX_NAME];
printf("请输入你要查找的联系人的姓名:");
scanf("%s", name);
int i = 0;
for (i = 0; i < ps->sz; i++)
{
if (strcmp(ps->data[i].name, name) == 0)
{
break;
}
}
return i;
}
void SearchContact(Contact* ps)//查找联系人
{
int pos = PanDuan(ps);
if (pos == 0)
{
return;
}
if (pos == ps->sz)
{
printf("没有你要查找的联系人!\n");
return;
}
printf("查找成功!,此人的信息如下:\n");
printf("%-5s\t%-4s\t%-4s\t%-15s\n", "姓名", "年龄", "性别", "电话");
printf("%-5s\t%-4d\t%-4s\t%-15s\n", ps->data[pos].name, ps->data[pos].age, ps->data[pos].sex, ps->data[pos].tele);
}
void ModifContact(Contact* ps)//修改联系人的信息
{
int pos = PanDuan(ps);
if (pos == 0)
{
return;
}
if (pos == ps->sz)
{
printf("没有你要修改的联系人!\n");
return;
}
printf("查找成功,请修改他的信息:\n");
printf("请输入联系人的姓名:");
scanf("%s", ps->data[pos].name);
printf("请输入联系人的年龄:");
scanf("%d", &(ps->data[pos].age));
printf("请输入联系人的性别:");
scanf("%s", ps->data[pos].sex);
printf("请输入联系人的电话:");
scanf("%s", ps->data[pos ].tele);
printf("修改成功!\n");
}
void DelContact(Contact* ps)//删除联系人的信息
{
int pos = PanDuan(ps);
if (pos == 0)
{
return;
}
if (pos == ps->sz)
{
printf("没有你要删除的联系人信息!\n");
return;
}
int j = pos;
for (j = pos; j < ps->sz - 1; j++)
{
ps->data[j] = ps->data[j + 1];
}
ps->sz--;
printf("删除成功!\n");
}
int cmp_name(const void* e1, const void* e2)
{
return strcmp(((PeoInfo*)e1)->name, ((PeoInfo*)e2)->name);
}
void SortContact(Contact* ps)//排序
{
qsort(ps->data, ps->sz,sizeof(PeoInfo), cmp_name);
printf("排序成功!\n");
}
void EmptyContact(Contact* ps)//清空
{
ps->sz = 0;
printf("清空成功!\n");
}
void ReadContact(Contact* ps)//读取文件
{
assert(ps);
FILE* pf = fopen("tongxunlu.txt", "r");
if (pf == NULL)
{
perror("fopen:");
return;
}
//读取文件
PeoInfo tmp = {
0 };
char c = 0;
int r_n = 0;
while (!feof(pf))
{
c = fgetc(pf);
if (c == '\n')
{
r_n++;
}
}
rewind(pf);
for (ps->sz = 0; ps->sz < r_n; ps->sz++)
{
CheakConTact(ps);
fscanf(pf, "%s %d %s %s", ps->data[ps->sz].name, &(ps->data[ps->sz].age), ps->data[ps->sz].sex, ps->data[ps->sz].tele);
}
printf("读取文件成功!\n");
fclose(pf);
pf = NULL;
}
void SaveContact(Contact* ps)
{
assert(ps);
FILE* pf = fopen("tongxunlu.txt", "w");
if (NULL == pf)
{
perror("fopen:");
return;
}
int i = 0;
for (i = 0; i < ps->sz; i++)
{
fprintf(pf, "%s %d %s %s\n", ps->data[i].name, ps->data[i].age, ps->data[i].sex, ps->data[i].tele);
}
fclose(pf);
pf = NULL;
printf("保存成功\n");
}
void DestroyContact(Contact* ps)//销毁
{
assert(ps);
free(ps->data);
ps->data = NULL;
ps->sz = 0;
ps->capicity = 0;
}
五、总结
本次的小项目我没有对每一行代码具体分析,因为我觉得只要之前的知识学会了就能看懂这个代码,学不会,我在怎么去讲解你还是不懂,按照我的思路一步步的来梳理逻辑,相信大家可以很好的独立的把这个小项目独立写出来,今天的分享九到此结束了,我们下篇再见!!