C语言实现通讯录(动态数据存储版本)
该通讯录可以根据输入信息的多少自动分配空间,通过文件操作实现对通讯录中数据的保存。
文章目录
一、代码讲解
1.前期准备
写代码之前需要对代码进行模块化,方便后期维护和管理,创建三个文件,第一个contact.h的头文件,这个头文件里包含了函数的定义,结构体的定义,以及一些常量的定义。第二个contact.c的文件中是对头文件中定义的函数功能的实现。第三个test.c的文件中包含了主函数,程序的运行逻辑。
2.逻辑串讲
首先,思考一下,我们所写的通讯录存放的是什么类型的数据?(你聪明的大脑袋一定瞬间就GET了),结构体,因为通讯录里的信息是一个复杂对象,包括姓名、性别、年龄、电话号码、家庭住址。所以我们要先创建一个结构体,包含了通讯录里每一个元素的相关信息,代码如下
struct peoInfo//这里我们把数字用常量定义在contact.h文件中
//后边出现的常量都是一个道理
{
char name[MAX_name];
char sex[MAX_sex];
int age;
char tele[MAX_tele];
char home[MAX_home];
};
好了,现在你成功创建了一个结构体,那么你再想想,通讯录里中总不可能只有一个人的联系方式吧,所以你要定义一个指针来指向一块可以动态开辟的空间,来存放多个人的联系方式,而且你总得知道你存了多少个人吧,所以你还要定义一个变量sz,来记录你存了几个联系人,因为我们还要实现通讯录的动态扩容,所以还要定义一个capacity变量(这个在暂时不用管,一会讲到动态扩容那里你自然就会知道为啥要定义它了,继续往下看铁子)。要用到这么多东西,所以我们再创建一个结构体struct contact:
struct contact
{
struct peoInfo* data;
int sz;
int capacity;
};
为了方便代码的维护和管理我所以我们把结构体的定义放到contact.h的头文件中。
好,现在移步主函数中我在代码中用注释进行讲解比较直观,建议复制到vs中方便观看:
#define _CRT_SECURE_NO_WARNINGS
#include"contact.h"
//因为你的结构体的定义,以及函数的声明都在这个头文件中,
//所以你要使用这些东西的话就要引入这个头文件
void menu()
{
printf("**************************************\n");
printf("***** 1. add 2. del *****\n");
printf("***** 3. search 4. modify *****\n");
printf("***** 5. show 6. sort *****\n");
printf("***** 0. exit *****\n");
printf("**************************************\n");
}//这部分是创建一个初始菜单
int main()
{
int input;//定义一个变量来接受你的选择
struct contact con;//定义一个通讯录结构体
Initcontact(&con);//对结构体进行初始化
//注意,我们先写逻辑然后再逐个实现功能,所以现在你就假设已经写好
//初始化函数,直接用就好啦。
menu();//打印菜单
do//一个do...while语句,用来实现多次选择,当选择0的时候即退出程序
//便不再循环
{
printf("please input your choice:>");
scanf("%d", &input);
switch (input)//在这里用到一个switch选择语句,来执行用户的选择
{
case 1:
Addcontact(&con);//添加操作
break;
case 2:
Deletcontact(&con);//删除操作
break;
case 3:
Searchcontact(&con);//查找操作
break;
case 4:
Modifycontact(&con);//修改联系人
break;
case 5:
Showcontact(&con);//展示通讯录
break;
case 6:
Sortcontact(&con);//排序通讯录
break;
case 0:
Savecontact(&con);//保存通讯录
Distroycontact(&con);//销毁开辟的空间(不着急,到这会说)
printf("Thank you for your using!\n");
break;
default:
printf("You have written the wrong number!\n");
break;
}
} while (input);
return 0;
}
好了,现在你已经把框架搭好了,接下来开始装修就好了,在这里先声明一下,函数的定义都是在头文件中声明,在contact.c文件中定义具体功能
eg:
//contact.h中声明
void Initcontact(struct contact* p);
//contact.c中定义功能
void Initcontact(struct contact* p)
{
assert(p);
p->data = (struct peoInfo*)malloc(DEFAULT_MAX * sizeof(struct peoInfo));
if (p->data == NULL)
{
perror("Initcontact()");
return;
}
p->capacity = DEFAULT_MAX;
p->sz = 0;
L
所以套路都一样,你瞅一眼下面源代码一下子就知道问我在说啥了,希望你耗子喂汁。
3.函数功能的实现
开整开整!
(一个能打的都没有,相信我,铁汁)
1).初始化函数:
int check(struct contact* p)
{
assert(p);//这个是检查传入是否为空指针的函数
//需要在contact.h中引入头文件assert.h
if (p->sz == p->capacity)
{
struct peoInfo* ptr = realloc(p->data, (p->capacity+SIZE)*sizeof(struct peoInfo));
//这时候就可以解释为什么要有一个capacity
//capacity是你初始给的空间大小,所以每当p->sz等于p->capacity
//就要用realloc函数进行扩容
if (ptr == NULL)
{
perror("Addcontact()");
return 0;
}//判断空间是否开辟成功
p->data = ptr;//将开辟好的ptr传给p->data
p->capacity += SIZE;//更新p->capacity的大小
}
return 1;
}
void Loadcontact(struct contact* p)
{
FILE* pf = fopen("contact.txt", "rb");
//创建一个文件指针,其对文件打开方式是读取二进制内容
//这个contact.txt函数就是用来存储你之前录入的通讯录
if (pf == NULL)
{
perror("fopen");
return 1;
}//判断是否开辟成功
struct peoInfo temp = { 0 };//定义一个联系人结构体
while (fread(&temp, sizeof(struct peoInfo), 1, pf))//读取文件中的内容到temp中,一下读取一个
//因为fread的返回值是读取成功个数,如果数据读取完了就会返回零,循环就结束了
{
//考虑增加容量的问题
check(p);//因为你从文件中导入数据到你当前程序中需要空间,
//所以你要检查你的空间够不够用,不够就要动态扩容
p->data[p->sz] = temp;//把temp的数据放到开辟的空间中
p->sz++;//元素个数增加
}
fclose(pf);//关闭文件
pf = NULL;//防止野指针的生成
}
void Initcontact(struct contact* p)
{
assert(p);
p->data = (struct peoInfo*)malloc(DEFAULT_MAX * sizeof(struct peoInfo));//开辟一块动态空间,这里我们把数字用常量定义在contact.h文件中
//后边出现的常量都是一个道理
if (p->data == NULL)
{
perror("Initcontact()");
return;
}
p->capacity = DEFAULT_MAX;//此时capacity为目前空间
p->sz = 0;
Loadcontact(p);//执行导入数据函数
}
2).增加联系人:
void Addcontact(struct contact* p)
{
assert(p);
check(p);//因为需要动态扩大空间,所以要检查一下是否需要扩容
printf("name:>");
scanf("%s", p->data[p->sz].name);
printf("sex:>");
scanf("%s", p->data[p->sz].sex);
printf("age:>");
scanf("%d", &p->data[p->sz].age);
printf("tele:>");
scanf("%s", p->data[p->sz].tele);
printf("home:>");
scanf("%s", p->data[p->sz].home);
p->sz++;
}
3).退出程序
void Savecontact(struct contact* p)//这个函数是为了把你写好的数据存起来
{
FILE* pf = fopen("contact.txt", "wb");//打开文件的方式为写入数据
if (pf == NULL)
{
perror("fopen");
return 1;
}
int i = 0;
for (i = 0; i < p->sz; i++)
{
fwrite(p->data+i, sizeof(struct peoInfo), 1, pf);
}
fclose(pf);
pf = NULL;
}
void Distroycontact(struct contact* p)
{
p->sz = 0;
p->capacity = 0;
free(p->data);
p->data = NULL;
}//因为之前用了malloc和realloc开辟了动态空间,所以现在用完要销毁
3).展示联系人
void Showcontact(struct contact* p)
{
assert(p);
printf("%-20s\t%-6s\t%-6s\t%-12s\t%-20s\t\n", "name", "sex", "age", "tele", "home");
//\t是水平制表符,-20是左对齐,20是个字节宽度
for (int i = 0; i < p->sz; i++)
{
printf("%-20s\t%-6s\t%-6d\t%-12s\t%-20s\t\n",
p->data[i].name,
p->data[i].sex,
p->data[i].age,
p->data[i].tele,
p->data[i].home
);
}
}
4).删除联系人
//不管是删除,修改,查找都需要先找到那个人,所以就写一个函数专门来找人
//下面要用到strcmp函数,所以要引入头文件string.h
int Findcontact(const struct contact* p, char str[])
{
for (int i = 0; i < p->sz; i++)
{
if (0 == strcmp(str, p->data[i].name))//strcmp函数比较如果相同返回值是0
{
return i;//返回对应位置的下标
}
}
return -1;
}
void Deletcontact(struct contact* p)
{
char str[MAX_name];
printf("please input the name:>");
scanf("%s", &str);
int ret = Findcontact(p, str);
if (ret == -1)
{
printf("we can't find the name you want!\n");
}
else
{
//因为删除一个联系人就相当于把后面的每一个联系人都前移一位所以使用循环就可以实现
for (int i = ret; i < p->sz - 1; i++)
{
p->data[i] = p->data[i + 1];
}
p->sz--;
printf("the name and the message has been deleted!\n");
}
}
5).查找联系人
//运用之前讲的内容就可以实现,先找出,然后再打印就好了
void Searchcontact(struct contact* p)
{
char name[MAX_name];
printf("Please input the name you want to search:>");
scanf("%s", &name);
int ret = Findcontact(p, name);
if (ret == -1)
{
printf("we can't find the name you want!\n");
}
else
{
printf("%-20s\t%-6s\t%-6s\t%-12s\t%-20s\t\n", "name", "sex", "age", "tele", "home");
printf("%-20s\t%-6s\t%-6d\t%-12s\t%-20s\t\n",
p->data[ret].name,
p->data[ret].sex,
p->data[ret].age,
p->data[ret].tele,
p->data[ret].home
);
}
}
6).修改联系人
//同样道理,一看就会
void Modifycontact(struct contact* p)
{
char name[MAX_name];
printf("Please input the name you want to modify:>");
scanf("%s", &name);
int ret = Findcontact(p, name);
if (ret == -1)
{
printf("we can't find the name you want!\n");
}
else
{
printf("name:>");
scanf("%s", p->data[ret].name);
printf("sex:>");
scanf("%s", p->data[ret].sex);
printf("age:>");
scanf("%d", &p->data[ret].age);
printf("tele:>");
scanf("%s", p->data[ret].tele);
printf("home:>");
scanf("%s", p->data[ret].home);
}
printf("We have modify the contact!\n");
}
7).排序联系人
int sortbyname(const void* e1, const void* e2)//这个是qsort函数所需要的比较函数
{
return strcmp(((struct peoInfo*)e1)->name, ((struct peoInfo*)e2)->name);
}
void Sortcontact(struct contact* p)
{
qsort(p->data, p->sz, sizeof(struct peoInfo), sortbyname);
//这里使用到qsort函数进行排序要引头文件stdlib.h
printf("we have sorted the contact!\n");
}
二、源代码
contact.h
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<assert.h>
#include<stdlib.h>
#define MAX_name 20
#define MAX_sex 6
#define MAX_tele 12
#define MAX_home 20
#define DEFAULT_MAX 3
#define SIZE 2
struct peoInfo
{
char name[MAX_name];
char sex[MAX_sex];
int age;
char tele[MAX_tele];
char home[MAX_home];
};
struct contact
{
struct peoInfo* data;
int sz;
int capacity;
};
void Initcontact(struct contact* p);
void Addcontact(struct contact* p);
void Showcontact(struct contact* p);
void Deletcontact(struct contact* p);
int Findcontact(struct contact* p);
void Searchcontact(struct contact* p);
void Modifycontact(struct contact* p);
void Sortcontact(struct contact* p);
void Distroycontact(struct contact* p);
void Savecontact(struct contact* p);
void Loadcontact(struct contact* p);
contact.c
#define _CRT_SECURE_NO_WARNINGS
#include"contact.h"
static int check(struct contact* p);
void Loadcontact(struct contact* p)
{
FILE* pf = fopen("contact.txt", "rb");
if (pf == NULL)
{
perror("fopen");
return 1;
}
struct peoInfo temp = { 0 };
while (fread(&temp, sizeof(struct peoInfo), 1, pf))
{
//考虑增加容量的问题
check(p);
p->data[p->sz] = temp;
p->sz++;
}
fclose(pf);
pf = NULL;
}
void Initcontact(struct contact* p)
{
assert(p);
p->data = (struct peoInfo*)malloc(DEFAULT_MAX * sizeof(struct peoInfo));
if (p->data == NULL)
{
perror("Initcontact()");
return;
}
p->capacity = DEFAULT_MAX;
p->sz = 0;
Loadcontact(p);
}
int check(struct contact* p)
{
assert(p);
if (p->sz == p->capacity)
{
struct peoInfo* ptr = realloc(p->data, (p->capacity+SIZE)*sizeof(struct peoInfo));
if (ptr == NULL)
{
perror("Addcontact()");
return 0;
}
p->data = ptr;
p->capacity += SIZE;
}
return 1;
}
void Distroycontact(struct contact* p)
{
p->sz = 0;
p->capacity = 0;
free(p->data);
p->data = NULL;
}
//动态版本
void Addcontact(struct contact* p)
{
assert(p);
check(p);
printf("name:>");
scanf("%s", p->data[p->sz].name);
printf("sex:>");
scanf("%s", p->data[p->sz].sex);
printf("age:>");
scanf("%d", &p->data[p->sz].age);
printf("tele:>");
scanf("%s", p->data[p->sz].tele);
printf("home:>");
scanf("%s", p->data[p->sz].home);
p->sz++;
}
void Showcontact(struct contact* p)
{
assert(p);
printf("%-20s\t%-6s\t%-6s\t%-12s\t%-20s\t\n", "name", "sex", "age", "tele", "home");
for (int i = 0; i < p->sz; i++)
{
printf("%-20s\t%-6s\t%-6d\t%-12s\t%-20s\t\n",
p->data[i].name,
p->data[i].sex,
p->data[i].age,
p->data[i].tele,
p->data[i].home
);
}
}
int Findcontact(const struct contact* p, char str[])
{
for (int i = 0; i < p->sz; i++)
{
if (0 == strcmp(str, p->data[i].name))
{
return i;
}
}
return -1;
}
void Deletcontact(struct contact* p)
{
char str[MAX_name];
printf("please input the name:>");
scanf("%s", &str);
int ret = Findcontact(p, str);
if (ret == -1)
{
printf("we can't find the name you want!\n");
}
else
{
for (int i = ret; i < p->sz - 1; i++)
{
p->data[i] = p->data[i + 1];
}
p->sz--;
printf("the name and the message has been deleted!\n");
}
}
void Searchcontact(struct contact* p)
{
char name[MAX_name];
printf("Please input the name you want to search:>");
scanf("%s", &name);
int ret = Findcontact(p, name);
if (ret == -1)
{
printf("we can't find the name you want!\n");
}
else
{
printf("%-20s\t%-6s\t%-6s\t%-12s\t%-20s\t\n", "name", "sex", "age", "tele", "home");
printf("%-20s\t%-6s\t%-6d\t%-12s\t%-20s\t\n",
p->data[ret].name,
p->data[ret].sex,
p->data[ret].age,
p->data[ret].tele,
p->data[ret].home
);
}
}
void Modifycontact(struct contact* p)
{
char name[MAX_name];
printf("Please input the name you want to modify:>");
scanf("%s", &name);
int ret = Findcontact(p, name);
if (ret == -1)
{
printf("we can't find the name you want!\n");
}
else
{
printf("name:>");
scanf("%s", p->data[ret].name);
printf("sex:>");
scanf("%s", p->data[ret].sex);
printf("age:>");
scanf("%d", &p->data[ret].age);
printf("tele:>");
scanf("%s", p->data[ret].tele);
printf("home:>");
scanf("%s", p->data[ret].home);
}
printf("We have modify the contact!\n");
}
int sortbyname(const void* e1, const void* e2)
{
return strcmp(((struct peoInfo*)e1)->name, ((struct peoInfo*)e2)->name);
}
void Sortcontact(struct contact* p)
{
qsort(p->data, p->sz, sizeof(struct peoInfo), sortbyname);
printf("we have sorted the contact!\n");
}
void Savecontact(struct contact* p)
{
FILE* pf = fopen("contact.txt", "wb");
if (pf == NULL)
{
perror("fopen");
return 1;
}
int i = 0;
for (i = 0; i < p->sz; i++)
{
fwrite(p->data+i, sizeof(struct peoInfo), 1, pf);
}
fclose(pf);
pf = NULL;
}
test.c
#define _CRT_SECURE_NO_WARNINGS
#include"contact.h"
void menu()
{
printf("**************************************\n");
printf("***** 1. add 2. del *****\n");
printf("***** 3. search 4. modify *****\n");
printf("***** 5. show 6. sort *****\n");
printf("***** 0. exit *****\n");
printf("**************************************\n");
}
int main()
{
int input;
struct contact con;
Initcontact(&con);
menu();
do
{
printf("please input your choice:>");
scanf("%d", &input);
switch (input)
{
case 1:
Addcontact(&con);
break;
case 2:
Deletcontact(&con);
break;
case 3:
Searchcontact(&con);
break;
case 4:
Modifycontact(&con);
break;
case 5:
Showcontact(&con);
break;
case 6:
Sortcontact(&con);
break;
case 0:
Savecontact(&con);
Distroycontact(&con);
printf("Thank you for your using!\n");
break;
default:
printf("You have written the wrong number!\n");
break;
}
} while (input);
return 0;
}
总结
以上便是我对通讯录代码的讲解,希望会对你有所帮助,其中有些像qsort函数呀,malloc,realloc动态开辟空间啊,文件指针啊之类的我会慢慢写写完会把链接放在这里,谢谢。