作者: 华丞臧.
专栏:【C语言】
各位读者老爷如果觉得博主写的不错,请诸位多多支持(点赞+收藏+关注
)。如果有错误的地方,欢迎在评论区指出。
文章目录
- 一、文件动态通讯录
- 二、相关代码解析
- 2.1 初始化
- 2.2 销毁通讯录
- 2.3 增加联系人
- 2.4 查找指定联系人
- 2.5 删除指定联系人
- 2.6 修改指定联系人
- 2.7 通讯录清空
- 2.8 按照姓名排序
- 三、代码
- 3.1 main.c
- 3.2 Contact.h
- 3.3 Contact.c
一、文件动态通讯录
前面写过一个静态通讯录,静态通讯录的容量是固定的,存放联系人达到最大容量就不能再存放了,并且在程序运行结束通讯录就销毁了;静态通讯录缺点很多,既不能根据用户需求扩容又不能在本地保存。
这次在学习相关文件操作后,我们来尝试写一个可以再本地文件保存数据的可动态增长的通讯录。
二、相关代码解析
2.1 初始化
初始化函数不仅要初始化data
数组,还需要把本地保存的联系人数据加载到通讯录当中;而加载数据到data
数组中去需要我们在堆上开辟空间,并且我们并不知道本地存储了多少字节的数据,所以写一个检查扩容的函数,当data
数组没有开辟内存或者存满了就扩容,一次扩容两倍。
使用的是fread
库函数,其函数原型如下:
//检查扩容
void CheckContactCapacity(CT* con)
{
assert(con);
//判断是否需要扩容
if (con->size == con->capacity)
{
int newCapacity = con->capacity * 2;
PeoInfo* tmp = (PeoInfo*)realloc(con->data, newCapacity * sizeof(PeoInfo));
if (tmp == NULL)
{
perror("realloc fail");
exit(-1);
}
con->data = tmp;
con->capacity = newCapacity;
printf("扩容成功\n");
}
}
//初始化
void ContactInit(CT* con)
{
assert(con);
con->data = (PeoInfo*)malloc(4 * sizeof(PeoInfo));
con->size = 0;
con->capacity = 4;
//memset(con->data, 0, con->capacity * sizeof(PeoInfo));
FILE* pf = fopen("contact.txt", "rb");
PeoInfo s = { 0 };
while (fread(&s, sizeof(PeoInfo), 1, pf))
{
CheckContactCapacity(con);
con->data[con->size] = s;
con->size++;
}
fclose(pf);
}
2.2 销毁通讯录
销毁通讯录,把通讯录当中的数据写入到本地文件当中并且释放动态内存开辟的空间。
在这里我使用的是fwrite
库函数,其函数原型如下:
//通讯录销毁
void ContactDestroy(CT* con)
{
assert(con);
if (con->size != 0)
{
FILE* pf = fopen("contact.txt", "wb");
int i = 0;
PeoInfo s = { 0 };
while (i < con->size)
{
s = con->data[i];
fwrite(&s, sizeof(PeoInfo), 1, pf);
i++;
}
fclose(pf);
}
free(con->data);
con->data = NULL;
con->size = con->capacity = 0;
}
2.3 增加联系人
通讯录是用来存储联系人信息的,那么就必然需要增、删、查、改等一系列接口,通过这些接口来对通讯录进行操作。那么在通讯录中增加一个新的联系人,首先需要判断是否需要扩容;在初始化函数当中已经写了CheckContactCapacity()
的函数直接调用即可,然后输入要增加的人的信息依次输入,用scanf
函数存入对应的data
数组当中。
//增加
void ContactAdd(CT* con)
{
assert(con);
//判断是否需要扩容
CheckContactCapacity(con);
//输入联系人信息
printf("请输入姓名->");
scanf("%s",con->data[con->size].name);
printf("请输入性别->");
scanf("%s", con->data[con->size].sex);
printf("请输入年龄->");
scanf("%d", &(con->data[con->size].age));
printf("请输入电话->");
scanf("%s", con->data[con->size].tele);
printf("请输入地址->");
scanf("%s", con->data[con->size].addr);
con->size++;
}
2.4 查找指定联系人
查找指定联系人,需要知道该联系人至少一个信息。既然是按照姓名查找,就写一个FindByName()
函数用来查找联系人,找到了返回下标
找不到返回-1
;找到了,就根据返回的下标把其对应的信息打印出来。
//按照名字查找,找到返回下标,找不到返回-1
int FindByName(CT* con,char* name)
{
assert(con);
PeoInfo* cur = con->data;
//遍历查找
int i = 0;
while (i < con->size)
{
if (strcmp(name, (cur + i)->name) == 0)
{
return i; //
}
i++;
}
return -1;
}
//查找
void ContactFind(CT* con)
{
assert(con);
char* name[NAME_MAX] = { 0 };
printf("请输入要查找人的姓名->");
scanf("%s",name);
int n = FindByName(con,name);
if (-1 == n)
{
printf("查找的人不存在\n");
return;
}
printf("%-20s\t %-5s\t %-3s\t %-12s\t %-30s\t\n",
"姓名",
"性别",
"年龄",
"电话",
"地址");
printf("%-20s\t %-5s\t %-3d\t %-12s\t %-30s\t\n"
, con->data[n].name
, con->data[n].sex
, con->data[n].age
, con->data[n].tele
, con->data[n].addr);
}
2.5 删除指定联系人
通过姓名删除指定联系人,调用FindByName()
函数找出要删除的人的下标,再挪动数据把data
数组处于下标位置的数据就可以达到删除的效果。
//删除
void ContactDel(CT* con)
{
assert(con);
assert(con->size);
char name[NAME_MAX] = { 0 };
printf("请输入要删除人的姓名->");
scanf("%s",name);
//查找
int n = FindByName(con, name);
if (-1 == n)
{
printf("删除的人不存在\n");
}
else
{
//挪动数据
while (n < con->size - 1)
{
con->data[n] = con->data[n + 1];
n++;
}
con->size--;
}
printf("删除成功\n");
}
2.6 修改指定联系人
这里也是通过姓名来查找并且修改联系人的信息,同样调用FindByName()
函数找出要修改的练习人的下标,通过返回的下标对其进行修改操作。
//修改
void ContactModify(CT* con)
{
assert(con);
char* name[NAME_MAX] = { 0 };
printf("请输入要修改人的姓名->");
scanf("%s",name);
int n = FindByName(con, name);
if (-1 == n)
{
printf("要修改的人不存在\n");
return;
}
//输入联系人信息
printf("请输入姓名->");
scanf("%s", con->data[n].name);
printf("请输入性别->");
scanf("%s", con->data[n].sex);
printf("请输入年龄->");
scanf("%d", &(con->data[n].age));
printf("请输入电话->");
scanf("%s", con->data[n].tele);
printf("请输入地址->");
scanf("%s", con->data[n].addr);
printf("修改成功\n");
}
2.7 通讯录清空
通讯录清空只需要把con->size
置为0
即可,con->size
表示data
数组中有效数据的个数。
//清空
void ContactClear(CT* con)
{
assert(con);
con->size = 0;
printf("清空成功\n");
}
2.8 按照姓名排序
利用qsort
函数,按照姓名对data
中存放的数据进行排序;这里需要我们写一个比较字符串大小的函数,而strcmp()
是用来比较字符串大小的库函数,且其返回值与qsort
的要求一致。
qsort的说明和使用可见👉:qsort的使用和实现
int cmp_by_name(char* str1, char* str2)
{
return strcmp(str1, str2);
}
//按照姓名排序
void ContactSortByName(CT* con)
{
assert(con);
qsort(con->data, con->size, sizeof(PeoInfo), cmp_by_name);
ContactShow(con);
}
三、代码
3.1 main.c
#include "Contact.h"
void menu()
{
printf("***** ***** ***** ***** *****\n");
printf("***** 0.退出 1.添加 *****\n");
printf("***** 2.删除 3.查找 *****\n");
printf("***** 4.修改 5.展示 *****\n");
printf("***** 6.排序 7.清空 *****\n");
printf("***** ***** ***** ***** *****\n");
}
int main()
{
CT con;
ContactInit(&con);
int input = 0;
do
{
menu();
printf("请选择->");
scanf("%d",&input);
switch (input)
{
case EXIT:
ContactDestroy(&con);
break;
case ADD:
ContactAdd(&con);
break;
case DEL:
ContactDel(&con);
break;
case SEARCH:
ContactSearch(&con);
break;
case MODIFY:
ContactModify(&con);
break;
case SHOW:
ContactShow(&con);
break;
case SORT:
ContactSortByName(&con);
break;
case CLEAR:
ContactClear(&con);
break;
default:
break;
}
} while (input);
return 0;
}
3.2 Contact.h
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#define NAME_MAX 20
#define SEX_MAX 5
#define TELE_MAX 12
#define ADDR_MAX 30
typedef struct PeopleInfo
{
char name[NAME_MAX];
char sex[SEX_MAX];
int age;
char* tele[TELE_MAX];
char* addr[ADDR_MAX];
}PeoInfo;
typedef struct Contact
{
PeoInfo* data;
int size; //联系人个数
int capacity; //容量
}CT;
enum Operations
{
EXIT,
ADD,
DEL,
SEARCH,
MODIFY,
SHOW,
SORT,
CLEAR
};
//初始化
void ContactInit(CT* con);
//打印通讯录信息
void ContactShow(CT* con);
//增加
void ContactAdd(CT* con);
//删除
void ContactDel(CT* con);
//查找
void ContactSearch(CT* con);
//修改
void ContactModify(CT* con);
//清空
void ContactClear(CT* con);
//按照姓名排序
void ContactSortByName(CT* con);
//通讯录销毁
void ContactDestroy(CT* con);
3.3 Contact.c
#include "Contact.h"
//检查扩容
void CheckContactCapacity(CT* con)
{
assert(con);
//判断是否需要扩容
if (con->size == con->capacity)
{
int newCapacity = con->capacity * 2;
PeoInfo* tmp = (PeoInfo*)realloc(con->data, newCapacity * sizeof(PeoInfo));
if (tmp == NULL)
{
perror("realloc fail");
exit(-1);
}
con->data = tmp;
con->capacity = newCapacity;
//memset(tmp+con->size, 0, (con->capacity-con->size) * sizeof(PeoInfo));
printf("扩容成功\n");
}
}
//初始化
void ContactInit(CT* con)
{
assert(con);
con->data = (PeoInfo*)malloc(4 * sizeof(PeoInfo));
con->size = 0;
con->capacity = 4;
//memset(con->data, 0, con->capacity * sizeof(PeoInfo));
FILE* pf = fopen("contact.txt", "rb");
PeoInfo s = { 0 };
while (fread(&s, sizeof(PeoInfo), 1, pf))
{
CheckContactCapacity(con);
con->data[con->size] = s;
con->size++;
}
fclose(pf);
}
//打印通讯录信息
void ContactShow(CT* con)
{
assert(con);
PeoInfo* cur = con->data;
//遍历打印
printf("%-20s\t %-5s\t %-3s\t %-12s\t %-30s\t\n", "姓名", "性别", "年龄", "电话", "地址");
int i = 0;
while (i < con->size)
{
printf("%-20s\t %-5s\t %-3d\t %-12s\t %-30s\t\n", con->data[i].name
, con->data[i].sex
, con->data[i].age
, con->data[i].tele
, con->data[i].addr);
i++;
}
}
//增加
void ContactAdd(CT* con)
{
assert(con);
//判断是否需要扩容
CheckContactCapacity(con);
//输入联系人信息
printf("请输入姓名->");
scanf("%s",con->data[con->size].name);
printf("请输入性别->");
scanf("%s", con->data[con->size].sex);
printf("请输入年龄->");
scanf("%d", &(con->data[con->size].age));
printf("请输入电话->");
scanf("%s", con->data[con->size].tele);
printf("请输入地址->");
scanf("%s", con->data[con->size].addr);
con->size++;
}
//按照名字查找,找到返回下标,找不到返回-1
int FindByName(CT* con,char* name)
{
assert(con);
PeoInfo* cur = con->data;
//遍历查找
int i = 0;
while (i < con->size)
{
if (strcmp(name, cur->name) == 0)
{
return i; //
}
i++;
}
return -1;
}
//删除
void ContactDel(CT* con)
{
assert(con);
assert(con->size);
char name[NAME_MAX] = { 0 };
printf("请输入要删除人的姓名->");
scanf("%s",name);
//查找
int n = FindByName(con, name);
if (-1 == n)
{
printf("删除的人不存在\n");
}
else
{
//挪动数据
while (n < con->size - 1)
{
con->data[n] = con->data[n + 1];
n++;
}
con->size--;
}
printf("删除成功\n");
}
//查找
void ContactSearch(CT* con)
{
assert(con);
char* name[NAME_MAX] = { 0 };
printf("请输入要查找人的姓名->");
scanf("%s",name);
int n = FindByName(con,name);
if (-1 == n)
{
printf("查找的人不存在\n");
return;
}
printf("%-20s\t %-5s\t %-3s\t %-12s\t %-30s\t\n",
"姓名",
"性别",
"年龄",
"电话",
"地址");
printf("%-20s\t %-5s\t %-3d\t %-12s\t %-30s\t\n"
, con->data[n].name
, con->data[n].sex
, con->data[n].age
, con->data[n].tele
, con->data[n].addr);
}
//修改
void ContactModify(CT* con)
{
assert(con);
char* name[NAME_MAX] = { 0 };
printf("请输入要修改人的姓名->");
scanf("%s",name);
int n = FindByName(con, name);
if (-1 == n)
{
printf("要修改的人不存在\n");
return;
}
//输入联系人信息
printf("请输入姓名->");
scanf("%s", con->data[n].name);
printf("请输入性别->");
scanf("%s", con->data[n].sex);
printf("请输入年龄->");
scanf("%d", &(con->data[n].age));
printf("请输入电话->");
scanf("%s", con->data[n].tele);
printf("请输入地址->");
scanf("%s", con->data[n].addr);
printf("修改成功\n");
}
//清空
void ContactClear(CT* con)
{
assert(con);
con->size = 0;
printf("清空成功\n");
}
int cmp_by_name(char* str1, char* str2)
{
return strcmp(str1, str2);
}
//按照姓名排序
void ContactSortByName(CT* con)
{
assert(con);
qsort(con->data, con->size, sizeof(PeoInfo), cmp_by_name);
ContactShow(con);
}
//通讯录销毁
void ContactDestroy(CT* con)
{
assert(con);
if (con->size != 0)
{
FILE* pf = fopen("contact.txt", "wb");
int i = 0;
PeoInfo s = { 0 };
while (i < con->size)
{
s = con->data[i];
fwrite(&s, sizeof(PeoInfo), 1, pf);
i++;
}
fclose(pf);
}
free(con->data);
con->data = NULL;
con->size = con->capacity = 0;
}