前言
C语言实现的小通讯录,可以文件保存,适合初学者拿来练手。
目录
1.功能及需求
首先这是个通讯录,我们需要一个菜单来展示通讯录功能和选择我们想要的功能,其次,一个通讯录要可以添加、修改、查找、删除、排序、展示、保存联系人,在我们使用完后联系人还要可以保存在文件中,使用前从文件中将联系人从文件中导入,所以我们有了以下的架构。
2.头文件&结构体&函数声明
//引头文件
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
//初始化常量
#define MAX_NAME 20//名字的最大长度
#define MAX_SEX 5//性别的最大长度
#define MAX_TELE 12//电话的最大长度
#define MAX_ADDR 40//地址的最大长度
#define DEFAULT_SZ 3//初始通讯录容量
#define ADD_SZ 2//每次扩容通讯录的量
//创建联系人结构体
struct PeoInfo
{
char name[MAX_NAME];
int age;
char sex[MAX_SEX];
char tele[MAX_TELE];
char addr[MAX_ADDR];
};
//创建通讯录结构体
struct Contact
{
struct PeoInfo* data;//联系人数据
int capaticy;//当前通讯录容量
int size;//当前联系人数量
};
//函数声明
void meum();//初始化菜单
void InitContact(struct Contact* ps);//初始化通讯录
void ShowContact(const struct Contact* ps);//展示通讯录
void AddContact(struct Contact* ps);//添加联系人
void DelContact(struct Contact* ps);//删除联系人
void SearchContact(const struct Contact* ps);//查找联系人
void ModifyContact(struct Contact* ps);//修改联系人信息
void SortContact(struct Contact* ps);//排序联系人
void DestoryContact(struct Contact* ps);//销毁通讯录
void SaveContact(struct Contact* ps);//保存通讯录
void LoadContact(struct Contact* ps);//加载通讯录到程序中
void CheckCapacity(struct Contact* ps);//查找通讯录是否需要增容
int FindByNmae(const struct Contact* ps, char* name);//在通讯录中查找联系人
(1)可以看到这里定义了很多宏常量,在后面如果想要修改一些参数,可以直接在#define这里修改,不用在程序里找出来再修改,效率会高很多,也很方便。
(2)这里创造了一个通讯录结构体,又创建了一个通讯录的结构体,成员包括通讯录的容量、联系人数量,还有一个联系人结构体的指针,这是因为如果一开始我们不知道通讯录中会存放多少联系人,就是如果通过创建struct PeoInfo类型的数组,我们不知道这个数组要定义多大,定义太大又不好,太小也不好,所以这里暂时先再通讯录结构体中放一个联系人结构体指针,通过动态内存分配分配通讯录的大小。
3.主函数&通讯录界面
int main()
{
char input;
struct Contact Con;//创建通讯录
InitContact(&Con);//初始化通讯录
do
{
meum();//打印菜单
setbuf(stdin, NULL);
input = getch();
switch(input)
{
case '1':
AddContact(&Con);//添加联系人
break;
case '2':
DelContact(&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);//保存通讯录
DestoryContact(&Con);//退出前free掉通讯录
break;
case '7':
SaveContact(&Con);//保存通讯录
break;
default:
printf("选择错误,请重新选择\n");
break;
}
} while (input!='0');
return 0;
}
//菜单
void meum()
{
printf("*********************************************\n");
printf("*****1.addcontact 2.delcontact *****\n");
printf("*****3.searchcontact 4.modifycontact*****\n");
printf("*****5.showcontact 6.sortcontact *****\n");
printf("*****7.save 0.exit *****\n");
printf("*********************************************\n");
}
(1)这里用了do—while循环里配合switch能非常好地实现菜单的选择功能,这里的input用%c类型时因为如果input时%d类型的话,在向input中输入的是字符或者字符串时就会陷入死循环,具体的大家可以自行编写尝试。
(2)里边用了一个setbuf函数,功能是清除输入缓冲区,如果一开始input接收的不是%c的内容,
这些内容有一部分就会留在输入缓冲区,导致下一次输入的时候getch直接读取了输入缓冲区的内容,出现错误,所以每次输入前都把输入缓冲区中的内容清空,清除输入缓冲区的方法有很多,这里不细谈,个人感觉setbuf(stdin,NULL)较为方便,stdin是标准输入流,即键盘输入。
4.初始化通讯录&加载已有通讯录
//初始化通讯录
void InitContact(struct Contact* ps)
{
ps->data = (struct PeoInfo*)malloc(DEFAULT_SZ * sizeof(struct PeoInfo));//用malloc进行动态内存分配,一开始先分配三个联系人的空间。
if (ps->data == NULL)//若分配失败
{
return;
}
ps->capaticy = DEFAULT_SZ;//初始化通讯录容量
ps->size = 0;//初始化联系人数量
LoadContact(ps);//从文件中加载已有通讯录进程序
}
//加载已有通讯录
void LoadContact(struct Contact* ps)
{
struct PeoInfo tmp = { 0 };
FILE* psRead = fopen("Contact.txt", "rb");//以二进制只读的方式打开文件
if (!psRead)//若打开失败,即psRead为空指针
{
printf("LoadContact::%s\n", strerror(errno));
return;
}
while (fread(&tmp, sizeof(struct PeoInfo), 1, psRead))//fread读取成功返回一个非0值,将文件中的数据先读入tmp中
{
CheckCapacity(ps);//每次一读取后检查程序中的通讯录是否需要增容
ps->data[ps->size] = tmp;//通讯录依次接收
ps->size++;//别忘了通讯录中联系人个数+1
}
fclose(psRead);//用完了就关闭文件
psRead = NULL;//指针置空,避免野指针
}
//检查通讯录是否需要增容
void CheckCapacity(struct Contact* ps)
{
if (ps->size == ps->capaticy)//如果通讯录的容量刚好等于联系人个数
{
struct PeoInfo* ptr = realloc(ps->data, (ps->capaticy + ADD_SZ) * sizeof(struct PeoInfo));//用realloc重新分配通讯录的大小(容纳联系人的个数)
if (ptr)//如果重新分配成功
{
ps->data = ptr;//原来的指针重新指向新分配的大空间
ps->capaticy += ADD_SZ;
printf("增容成功!\n");
}
else
{
printf("增容失败!\n");
}
}
}
首先先初始化通讯录的一些数据,再从文件中读取已有的联系人,若一开始通讯录分配的空间不足以容纳,则用relloc再开辟一块大空间存放。
5.通过名字查找联系人
//查找名字
int FindByNmae(const struct Contact* ps,char* name)
{
int i = 0;
for (i = 0; i < ps->size; i++)//将用户输入的name在通讯录中依次查找
{
if (strcmp(ps->data[i].name, name) == 0)//strcmp找到了相同的name,则返回0
{
return i;//找到了是在ps->data中的第几个
}
}
return -1;
}
这个功能在下面会用到很多次,单独包装成一个函数
6.添加联系人
void AddContact(struct Contact* ps)
{
CheckCapacity(ps);//检查是否需要增容
printf("请输入名字>");
scanf("%s", ps->data[ps->size].name);
printf("请输入年龄>");
scanf("%d", &(ps->data[ps->size].age));
printf("请输入性别>");
scanf("%s", ps->data[ps->size].sex);
printf("请输入电话>");
scanf("%s", ps->data[ps->size].tele);
printf("请输入地址>");
scanf("%s", ps->data[ps->size].addr);
ps->size++;
printf("添加成功\n");
}
这个比较简单
7.删除联系人
//删除联系人
void DelContact(struct Contact* ps)
{
char name[MAX_NAME]="";
printf("请输入你要删除的联系人的名字:\n");
scanf("%s", name);
int ret = FindByNmae(ps, name);//找到了联系人,返回-1
if (ret==-1)
{
printf("没有找到该联系人\n");
}
else
{
int j = 0;
for (j = ret; j < ps->size - 1; j++)//将后面的联系人往前送
{
ps->data[j] = ps->data[j + 1];
}
ps->size--;
printf("删除成功\n");
}
}
就是在列表中找到联系人,再把他删了。
8.查找联系人并将他打印出来
//查找联系人
void SearchContact(const struct Contact* ps)
{
char name[MAX_NAME];
printf("请输入要查找的名字");
scanf("%s", name);
int ret=FindByNmae(ps, name);
if (ret == -1)
{
printf("没找到");
}
else
{
printf("找到了!\n");
printf("%-20s\t%-4s\t%-5s\t%-12s\t%-20s\n", "名字", "年龄", "性别", "电话", "地址");
printf("%-20s\t%-4d\t%-5s\t%-12s\t%-20s\n",
ps->data[ret].name,
ps->data[ret].age,
ps->data[ret].sex,
ps->data[ret].tele,
ps->data[ret].addr);
}
}
9.修改联系人信息
//修改联系人信息
void ModifyContact(struct Contact* ps)
{
char name[MAX_NAME];
printf("请输入要修改的联系人");
scanf("%s", name);
int ret = FindByNmae(ps, name);
if (ret == -1)
{
printf("输入的联系人不存在\n");
}
else
{
printf("找到了,请重新输入>\n");
printf("请输入名字>");
scanf("%s", ps->data[ret].name);
printf("请输入年龄>");
scanf("%d", &(ps->data[ret].age));
printf("请输入性别>");
scanf("%s", ps->data[ret].sex);
printf("请输入电话>");
scanf("%s", ps->data[ret].tele);
printf("请输入地址>");
scanf("%s", ps->data[ret].addr);
printf("修改成功!\n");
}
}
10.展示通讯录
void ShowContact(const struct Contact* ps)
{
if (ps->size == 0)
{
printf("通讯录为空\n");
}
else
{
printf("%-20s\t%-4s\t%-5s\t%-12s\t%-20s\n", "名字", "年龄", "性别", "电话", "地址");
for (int i = 0; i < ps->size; i++)
{
printf("%-20s\t%-4d\t%-5s\t%-12s\t%-20s\n",
ps->data[i].name,
ps->data[i].age,
ps->data[i].sex,
ps->data[i].tele,
ps->data[i].addr);
}
}
}
11.排序通讯录
//排序比较函数
static int Cmp_Contact_Name(const void* e1, const void* e2)
{
return (((struct PeoInfo*)e2)->age) - (((struct PeoInfo*)e1)->age);
}
//依据年龄排序联系人
void SortContact(struct Contact* ps)
{
qsort((ps->data), ps->size,sizeof(ps->data[0]), Cmp_Contact_Name);
printf("排序完成!\n");
}
这里用了qsort快速排序库函数,按照用户的年龄进行排序,也可以按照其它的方式排序,详细的可以看博主的另一篇关于快速排序库函数qsort的介绍的文章。
12.保存通讯录到文件中
//保存通讯录到文件中
void SaveContact(struct Contact* ps)
{
FILE* pfWrite = fopen("Contact.txt", "wb");//以二进制只写的方式打开文件
if (!pfWrite)//若打开失败
{
printf("SaveContact::%s\n", strerror(errno));//errno可以理解成错误的编码,strerror可以将errno错误编码所对应的错误信息输出出来
return;
}
for (int i = 0; i < ps->size; i++)
{
fwrite(&(ps->data[i]), sizeof(struct PeoInfo), 1, pfWrite);//依次将信息写入
}
fclose(pfWrite);//写完就关闭文件
pfWrite = NULL;//置空,避免野指针
printf("保存成功!\n");
}
13.销毁通讯录(释放内存)
因为这个通讯录在程序中是用动态内存分配出来的,程序结束时要把这块空间free掉,还给系统。
//销毁通讯录-释放堆区内存
void DestoryContact(struct Contact* ps)
{
free(ps->data);
ps->data = NULL;
printf("退出成功!\n");
}
14.!!!!!全部代码!!!!
//引头文件
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
//初始化常量
#define MAX_NAME 20//名字的最大长度
#define MAX_SEX 5//性别的最大长度
#define MAX_TELE 12//电话的最大长度
#define MAX_ADDR 40//地址的最大长度
#define DEFAULT_SZ 3//初始通讯录容量
#define ADD_SZ 2//每次扩容通讯录的量
//创建联系人结构体
struct PeoInfo
{
char name[MAX_NAME];
int age;
char sex[MAX_SEX];
char tele[MAX_TELE];
char addr[MAX_ADDR];
};
//创建通讯录结构体
struct Contact
{
struct PeoInfo* data;//联系人数据
int capaticy;//当前通讯录容量
int size;//当前联系人数量
};
//函数声明
void meum();//初始化菜单
void InitContact(struct Contact* ps);//初始化通讯录
void ShowContact(const struct Contact* ps);//展示通讯录
void AddContact(struct Contact* ps);//添加联系人
void DelContact(struct Contact* ps);//删除联系人
void SearchContact(const struct Contact* ps);//查找联系人
void ModifyContact(struct Contact* ps);//修改联系人信息
void SortContact(struct Contact* ps);//排序联系人
void DestoryContact(struct Contact* ps);//销毁通讯录
void SaveContact(struct Contact* ps);//保存通讯录
void LoadContact(struct Contact* ps);//加载通讯录到程序中
void CheckCapacity(struct Contact* ps);//查找通讯录是否需要增容
int FindByNmae(const struct Contact* ps, char* name);//在通讯录中查找联系人
int main()
{
char input;
struct Contact Con;//创建通讯录
InitContact(&Con);//初始化通讯录
do
{
meum();//打印菜单
setbuf(stdin, NULL);
input = getch();
switch(input)
{
case '1':
AddContact(&Con);//添加联系人
break;
case '2':
DelContact(&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);//保存通讯录
DestoryContact(&Con);//退出前free掉通讯录
break;
case '7':
SaveContact(&Con);//保存通讯录
break;
default:
printf("选择错误,请重新选择\n");
break;
}
} while (input!='0');
return 0;
}
//菜单
void meum()
{
printf("*********************************************\n");
printf("*****1.addcontact 2.delcontact *****\n");
printf("*****3.searchcontact 4.modifycontact*****\n");
printf("*****5.showcontact 6.sortcontact *****\n");
printf("*****7.save 0.exit *****\n");
printf("*********************************************\n");
}
//初始化通讯录
void InitContact(struct Contact* ps)
{
ps->data = (struct PeoInfo*)malloc(DEFAULT_SZ * sizeof(struct PeoInfo));//用malloc进行动态内存分配,一开始先分配三个联系人的空间。
if (ps->data == NULL)//若分配失败
{
return;
}
ps->capaticy = DEFAULT_SZ;//初始化通讯录容量
ps->size = 0;//初始化联系人数量
LoadContact(ps);//从文件中加载已有通讯录进程序
}
//加载已有通讯录
void LoadContact(struct Contact* ps)
{
struct PeoInfo tmp = { 0 };
FILE* psRead = fopen("Contact.txt", "rb");//以二进制只读的方式打开文件
if (!psRead)//若打开失败,即psRead为空指针
{
printf("LoadContact::%s\n", strerror(errno));
return;
}
while (fread(&tmp, sizeof(struct PeoInfo), 1, psRead))//fread读取成功返回一个非0值,将文件中的数据先读入tmp中
{
CheckCapacity(ps);//每次一读取后检查程序中的通讯录是否需要增容
ps->data[ps->size] = tmp;//通讯录依次接收
ps->size++;//别忘了通讯录中联系人个数+1
}
fclose(psRead);//用完了就关闭文件
psRead = NULL;//指针置空,避免野指针
}
//检查通讯录是否需要增容
void CheckCapacity(struct Contact* ps)
{
if (ps->size == ps->capaticy)//如果通讯录的容量刚好等于联系人个数
{
struct PeoInfo* ptr = realloc(ps->data, (ps->capaticy + ADD_SZ) * sizeof(struct PeoInfo));//用realloc重新分配通讯录的大小(容纳联系人的个数)
if (ptr)//如果重新分配成功
{
ps->data = ptr;//原来的指针重新指向新分配的大空间
ps->capaticy += ADD_SZ;
printf("增容成功!\n");
}
else
{
printf("增容失败!\n");
}
}
}
//查找名字
int FindByNmae(const struct Contact* ps,char* name)
{
int i = 0;
for (i = 0; i < ps->size; i++)//将用户输入的name在通讯录中依次查找
{
if (strcmp(ps->data[i].name, name) == 0)//strcmp找到了相同的name,则返回0
{
return i;//找到了是在ps->data中的第几个
}
}
return -1;
}
//添加联系人
void AddContact(struct Contact* ps)
{
CheckCapacity(ps);//检查是否需要增容
printf("请输入名字>");
scanf("%s", ps->data[ps->size].name);
printf("请输入年龄>");
scanf("%d", &(ps->data[ps->size].age));
printf("请输入性别>");
scanf("%s", ps->data[ps->size].sex);
printf("请输入电话>");
scanf("%s", ps->data[ps->size].tele);
printf("请输入地址>");
scanf("%s", ps->data[ps->size].addr);
ps->size++;
printf("添加成功\n");
}
//删除联系人
void DelContact(struct Contact* ps)
{
char name[MAX_NAME]="";
printf("请输入你要删除的联系人的名字:\n");
scanf("%s", name);
int ret = FindByNmae(ps, name);//找到了联系人,返回-1
if (ret==-1)
{
printf("没有找到该联系人\n");
}
else
{
int j = 0;
for (j = ret; j < ps->size - 1; j++)//将后面的联系人往前送
{
ps->data[j] = ps->data[j + 1];
}
ps->size--;
printf("删除成功\n");
}
}
//查找联系人
void SearchContact(const struct Contact* ps)
{
char name[MAX_NAME];
printf("请输入要查找的名字");
scanf("%s", name);
int ret=FindByNmae(ps, name);
if (ret == -1)
{
printf("没找到");
}
else
{
printf("找到了!\n");
printf("%-20s\t%-4s\t%-5s\t%-12s\t%-20s\n", "名字", "年龄", "性别", "电话", "地址");
printf("%-20s\t%-4d\t%-5s\t%-12s\t%-20s\n",
ps->data[ret].name,
ps->data[ret].age,
ps->data[ret].sex,
ps->data[ret].tele,
ps->data[ret].addr);
}
}
//展示通讯录
void ShowContact(const struct Contact* ps)
{
if (ps->size == 0)
{
printf("通讯录为空\n");
}
else
{
printf("%-20s\t%-4s\t%-5s\t%-12s\t%-20s\n", "名字", "年龄", "性别", "电话", "地址");
for (int i = 0; i < ps->size; i++)
{
printf("%-20s\t%-4d\t%-5s\t%-12s\t%-20s\n",
ps->data[i].name,
ps->data[i].age,
ps->data[i].sex,
ps->data[i].tele,
ps->data[i].addr);
}
}
}
//排序比较函数
static int Cmp_Contact_Name(const void* e1, const void* e2)
{
return (((struct PeoInfo*)e2)->age) - (((struct PeoInfo*)e1)->age);
}
//依据年龄排序联系人
void SortContact(struct Contact* ps)
{
qsort((ps->data), ps->size,sizeof(ps->data[0]), Cmp_Contact_Name);
printf("排序完成!\n");
}
//保存通讯录到文件中
void SaveContact(struct Contact* ps)
{
FILE* pfWrite = fopen("Contact.txt", "wb");//以二进制只写的方式打开文件
if (!pfWrite)//若打开失败
{
printf("SaveContact::%s\n", strerror(errno));//errno可以理解成错误的编码,strerror可以将errno错误编码所对应的错误信息输出出来
return;
}
for (int i = 0; i < ps->size; i++)
{
fwrite(&(ps->data[i]), sizeof(struct PeoInfo), 1, pfWrite);//依次将信息写入
}
fclose(pfWrite);//写完就关闭文件
pfWrite = NULL;//置空,避免野指针
printf("保存成功!\n");
}
//销毁通讯录-释放堆区内存
void DestoryContact(struct Contact* ps)
{
free(ps->data);
ps->data = NULL;
printf("退出成功!\n");
}
看完了不妨点点赞吧~