用C语言实现通讯录管理系统,最终的数据保存在文件中,以实现永久化存储
一、简要说明
本系统的实现是在C语言实现通讯录管理系统(动态开辟内存)的基础上增加了对文件操作的相关函数,所以本文只对增加和修改的函数做说明
二、系统实现
(一)写数据到文件中
在程序退出前(退出系统前)将数据全部写到文件中,整个逻辑控制通过函数save实现,save中调用库函数fprintf将数据写入,fprintf函数的功能是将格式化的数据写到流中
//保存数据到文件中
void save(Contact* pcon)
{
//打开文件
FILE* pf = fopen("data.txt", "w");
if (pf == NULL) //如果为NULL说明打开文件失败
{
perror("fopen"); //输出错误信息
return;
}
//写文件
//采用fprintf函数进行写,该函数是功能是:将格式化的数据输出到对应的流中
int i = 0;
for (i = 0; i < pcon->sz; i++)
{
fprintf(pf, "%-10s %-8s %-8d %-15s %-15s\n", pcon->data[i].name, pcon->data[i].sex, pcon->data[i].age, pcon->data[i].tele, pcon->data[i].addr); //使用库函数
}
//关闭文件
fclose(pf);
pf = NULL;
}
(二)读数据到内存中(程序中)
对于读取数据,在程序刚开始的时候就应该将数据读取到程序中,所以我在初始化通讯录函数initContact中调用loadData函数,loadData函数主要实现将文件中的所有数据读取到程序中的控制,具体读取数据采用库函数fscanf,该函数的功能是从流中将格式化的数据加载到分配好的内存中,往往和fprintf配合使用
注意:如果第一次运行程序,会出现打开文件失败,因为第一次还没有建立存储数据的文件,不过没有关系
//从文件中加载数据到程序中
void loadData(Contact* pcon)
{
//打开文件
FILE* pf = fopen("data.txt", "r");
if (pf == NULL)
{
perror("fopen");
return;
}
//读文件
//以格式化的形式读取数据:因为我保存数据到文件中的时候采用的是fprintf(将格式化的数据保存到文件中),所以我读取的时候相应的读出来就行
char name[10]; //姓名
int age; //年龄
char sex[5]; //性别
char tele[12]; //电话
char addr[20]; //地址
int ret = 0;
while (fscanf(pf, "%s %s %d %s %s\n", name, sex, &age, tele, addr) == 5) //先将数据读到临时变量中,如果读取成功,该函数返回参数列表中成功填充的项数
{
if (pcon->sz + 1 > pcon->capacity) //每次读取一条数据前先检查空间是否够用
{
//需要扩容
increaseCapcity(pcon, 1);
}
//保存数据到通讯录中
strcpy(pcon->data[pcon->sz].name, name);
strcpy(pcon->data[pcon->sz].sex, sex);
pcon->data[pcon->sz].age = age;
strcpy(pcon->data[pcon->sz].tele, tele);
strcpy(pcon->data[pcon->sz].addr, addr);
pcon->sz++; //跟新sz
}
if (ferror(pf)) //检查是否读取错误而结束,是则返回非0
{
perror("fscanf");
return;
}
else if(feof(pf))
{
printf("【加载文件成功!】\n");
}
//关闭文件
fclose(pf);
pf = NULL;
}
//初始化通讯录
void initContact(Contact* pcon) //pcon指向静态开辟的通讯录
{
//先将通讯录初始化
pcon->sz = 0; //初始化联系人个数为0
pcon->capacity = DEFAULT_MAX; //初始化通讯录容量,DEFAULT_MAX是contact.h头文件中定义的一个宏,表示通讯录初始最大容量
PerInfo* p = (PerInfo*)calloc(DEFAULT_MAX, sizeof(PerInfo)); //动态开辟存放联系人的空间
if (p == NULL) //开辟失败
{
perror("calloc"); //打印失败信息
return;
}
pcon->data = p; //让pcon->data指向开辟的空间
//将文件中的数据加载到程序中
loadData(pcon);
}
三、系统实现完整代码
通过链接C语言实现通讯录管理系统(文件版本)也可获取系统源码
(一)test.c文件
//实现通讯录,最终将数据以文件的形式存储,永久保留
#include "contact.h"
//主菜单函数
void menu()
{
printf("*****************************************\n");
printf("***** 欢迎使用通讯录 ******\n");
printf("***** 1.add 2.delete ******\n");
printf("***** 3.search 4.modify ******\n");
printf("***** 5.show 6.empty ******\n");
printf("***** 7.sort 0.exit ******\n");
printf("*****************************************\n");
}
//主函数
int main()
{
int input = 0;
//采用函数指针数组的形式代替switch—case语句,来控制程序的整体逻辑
//定义一个函数指针数组,数组中的每个元素是函数指针,函数的形参是Contact*,返回值是void
void (*arr[8]) (Contact*) = { NULL, addContact, delContact, searchContact, modifyContact, showContact, emptyContact, sortContact };
Contact con; //创建一个通讯录
initContact(&con); //初始话通讯录
do
{
menu();
printf("请输入你的选择:>");
scanf("%d", &input);
if (input > 0 && input < 8)
{
arr[input](&con);
}
else if (input == 0)
{
printf("退出程序!\n");
save(&con); //退出程序之前将数据保存到文件中
free(con.data); //释放动态开辟内存
break;
}
else
{
printf("输入错误,请重新输入!\n");
}
} while (input);
return 0;
}
(二)contact.h文件
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define DEFAULT_MAX 2 //通讯录的默认大小
#define SCALE 2 //通讯录每次扩容增加的幅度
//联系人的信息
typedef struct PerInfo
{
char name[10]; //姓名
int age; //年龄
char sex[5]; //性别
char tele[12]; //电话
char addr[20]; //地址
}PerInfo;
//通讯录
typedef struct Contact
{
int sz; //通讯录中人的个数
int capacity; //通讯录最大容量
PerInfo* data; //指向存放联系人信息的数组,数组空间动态开辟
}Contact;
//初始化通讯录
void initContact(Contact* pcon);
//添加联系人
void addContact(Contact* pcon);
//删除联系人
void delContact(Contact* pcon);
//查找联系人
void searchContact(const Contact* pcon);
//修改联系人
void modifyContact(Contact* pcon);
//显示全部联系人
void showContact(const Contact* pcon);
//清空联系人
void emptyContact(Contact* pcon);
//排序联系人
void sortContact(Contact* pcon);
//保存数据到文件中
void save(Contact* pcon);
(三)contact.c文件
#include "contact.h"
//通讯录扩容
void increaseCapcity(Contact* pcon, int n)//n是本次要批量添加联系人的个数
{
PerInfo* p = (PerInfo*)realloc(pcon->data, (pcon->capacity + SCALE + n) * sizeof(PerInfo)); //SCALE是contact.h头文件中定义的一个宏,表示通讯录中存放联系人的数组每次扩容的一个增量
if (p != NULL) //扩容成功
{
pcon->data = p;
pcon->capacity += SCALE + n; //更新通讯录最大容量
printf("【扩容成功!】\n");
}
else
{
perror("realloc"); //输出错误信息
}
}
//从文件中加载数据到程序中
void loadData(Contact* pcon)
{
//打开文件
FILE* pf = fopen("data.txt", "r");
if (pf == NULL)
{
perror("fopen");
return;
}
//读文件
//以格式化的形式读取数据:因为我保存数据到文件中的时候采用的是fprintf(将格式化的数据保存到文件中),所以我读取的时候相应的读出来就行
char name[10]; //姓名
int age; //年龄
char sex[5]; //性别
char tele[12]; //电话
char addr[20]; //地址
int ret = 0;
while (fscanf(pf, "%s %s %d %s %s\n", name, sex, &age, tele, addr) == 5) //先将数据读到临时变量中,如果读取成功,该函数返回参数列表中成功填充的项数
{
if (pcon->sz + 1 > pcon->capacity) //每次读取一条数据前先检查空间是否够用
{
//需要扩容
increaseCapcity(pcon, 1);
}
//保存数据到通讯录中
strcpy(pcon->data[pcon->sz].name, name);
strcpy(pcon->data[pcon->sz].sex, sex);
pcon->data[pcon->sz].age = age;
strcpy(pcon->data[pcon->sz].tele, tele);
strcpy(pcon->data[pcon->sz].addr, addr);
pcon->sz++; //跟新sz
}
if (ferror(pf)) //检查是否读取错误而结束,是则返回非0
{
perror("fscanf");
return;
}
else if(feof(pf))
{
printf("【加载文件成功!】\n");
}
//关闭文件
fclose(pf);
pf = NULL;
}
//初始化通讯录
void initContact(Contact* pcon) //pcon指向静态开辟的通讯录
{
//先将通讯录初始化
pcon->sz = 0; //初始化联系人个数为0
pcon->capacity = DEFAULT_MAX; //初始化通讯录容量,DEFAULT_MAX是contact.h头文件中定义的一个宏,表示通讯录初始最大容量
PerInfo* p = (PerInfo*)calloc(DEFAULT_MAX, sizeof(PerInfo)); //动态开辟存放联系人的空间
if (p == NULL) //开辟失败
{
perror("calloc"); //打印失败信息
return;
}
pcon->data = p; //让pcon->data指向开辟的空间
//将文件中的数据加载到程序中
loadData(pcon);
}
//按姓名查找
int* searchByName(const Contact* pcon)
{
char name[20] = { 0 };
int* arr = (int*)calloc(pcon->capacity + 1, sizeof(int)); //记录查找到的联系人下标,数组第一个元素记录查找到了多少个人,后面的元素是查找到的联系人下标
printf("请输入姓名:");
scanf("%s", name);
int i = 0;
for (i = 0; i < pcon->sz; i++)
{
if (strcmp(pcon->data[i].name, name) == 0)
{
arr[++arr[0]] = i;
}
}
return arr;
}
//按年龄查找
int* searchByAge(const Contact* pcon)
{
int age = 0;
int* arr = (int*)calloc(pcon->capacity + 1, sizeof(int));
printf("请输入年龄:");
scanf("%d", &age);
int i = 0;
for (i = 0; i < pcon->sz; i++)
{
if (pcon->data[i].age == age)
{
arr[++arr[0]] = i;
}
}
return arr;
}
//按性别查找
int* searchBySex(const Contact* pcon)
{
char sex[5] = { 0 };
int* arr = (int*)calloc(pcon->capacity + 1, sizeof(int));
printf("请输入性别:");
scanf("%s", sex);
int i = 0;
for (i = 0; i < pcon->sz; i++)
{
if (strcmp(pcon->data[i].sex, sex) == 0)
{
arr[++arr[0]] = i;
}
}
return arr;
}
//按电话查找
int* searchByTele(const Contact* pcon)
{
char tele[12] = { 0 };
int* arr = (int*)calloc(pcon->capacity + 1, sizeof(int));
printf("请输入电话:");
scanf("%s", tele);
int i = 0;
for (i = 0; i < pcon->sz; i++)
{
if (strcmp(pcon->data[i].tele, tele) == 0)
{
arr[++arr[0]] = i;
}
}
return arr;
}
//按地址查找
int* searchByAddr(const Contact* pcon)
{
char addr[20] = { 0 };
int* arr = (int*)calloc(pcon->capacity + 1, sizeof(int));
printf("请输入地址:");
scanf("%s", addr);
int i = 0;
for (i = 0; i < pcon->sz; i++)
{
if (strcmp(pcon->data[i].addr, addr) == 0)
{
arr[++arr[0]] = i;
}
}
return arr;
}
//添加联系人
void addContact(Contact* pcon)
{
printf("【添加联系人开始!】\n");
int n = 0;
printf("请输入你要添加几个联系人:>");
scanf("%d", &n);
if (pcon->sz + n > pcon->capacity) //通讯录已满,需要扩容
{
increaseCapcity(pcon, n);
}
int i = 0;
for (i = 0; i < n; i++)
{
printf("正在添加第%d个联系人:\n", i + 1);
printf("请输入姓名:");
scanf("%s", pcon->data[pcon->sz].name);
printf("请输入性别:");
scanf("%s", pcon->data[pcon->sz].sex);
printf("请输入年龄:");
scanf("%d", &pcon->data[pcon->sz].age);
printf("请输入电话:");
scanf("%s", pcon->data[pcon->sz].tele);
printf("请输入地址:");
scanf("%s", pcon->data[pcon->sz].addr);
pcon->sz++;
printf("第%d个联系人添加结束!\n", i + 1);
}
printf("【添加联系人结束,总共添加了%d个联系人!】\n", n);
}
//删除联系人菜单
void deleMenu()
{
printf("*****************************************\n");
printf("***** 请选择你要按什么条件删除 ******\n");
printf("***** 1.name 2.age ******\n");
printf("***** 3.tele 4.addr ******\n");
printf("***** 0.exit ******\n");
printf("*****************************************\n");
}
//根据选项删除联系人
void deleByCondition(Contact* pcon, int input)
{
//定义一个函数指针数组,将每个查找函数作为数组元素
int* (*fun[5])(Contact*) = { NULL, searchByName, searchByAge, searchByTele, searchByAddr };
int* arr = fun[input](pcon);
if (arr[0] == 1) //没有重复的联系人,直接删除
{
int k = 0;
for (k = arr[1]; k < pcon->sz - 1; k++) //删除
{
pcon->data[k] = pcon->data[k + 1];
}
}
else if (arr[0] == 0)
{
printf("没有该联系人!\n");
return;
}
else
{
printf("%-6s %-10s %-8s %-8s %-15s %-15s\n", "编号", "姓名", "性别", "年龄", "电话", "地址"); //打印标题
int i = 0;
for (i = 1; i <= arr[0]; i++)
{
printf("%-6d %-10s %-8s %-8d %-15s %-15s\n", i, pcon->data[arr[i]].name, pcon->data[arr[i]].sex, pcon->data[arr[i]].age, pcon->data[arr[i]].tele, pcon->data[arr[i]].addr);
}
while (1)
{
printf("联系人有多个,请输入你要删除的联系人编号:");
int n = 0;
scanf("%d", &n);
if (n >= 1 && n <= arr[0]) //判断输入是否合法
{
int k = 0;
for (k = arr[n]; k < pcon->sz - 1; k++) //删除
{
pcon->data[k] = pcon->data[k + 1];
}
break;
}
else
{
printf("输入错误,请重新输入!\n");
}
}
}
pcon->sz--;
printf("【成功删除联系人!】\n");
}
//删除联系人
void delContact(Contact* pcon)
{
int input = 0;
do
{
deleMenu();
printf("请输入你的选择:");
scanf("%d", &input);
if (input > 0 && input < 5)
{
deleByCondition(pcon, input);
}
else if (input == 0)
{
printf("退出删除联系人!\n");
break;
}
else
{
printf("输入错误,请重新输入!\n");
}
} while (input);
}
//查找联系人菜单
void searchMenu()
{
printf("*****************************************\n");
printf("***** 请选择你要按什么条件查找 ******\n");
printf("***** 1.name 2.age ******\n");
printf("***** 3.sex 4.tele ******\n");
printf("***** 5.addr 0.exit ******\n");
printf("*****************************************\n");
}
//打印查找到的联系人
void printInfo(const Contact* pcon, int* arr)
{
printf("%-10s %-8s %-8s %-15s %-15s\n", "姓名", "性别", "年龄", "电话", "地址"); //打印标题
int i = 0;
for (i = 1; i <= arr[0]; i++)
{
printf("%-10s %-8s %-8d %-15s %-15s\n", pcon->data[arr[i]].name, pcon->data[arr[i]].sex, pcon->data[arr[i]].age, pcon->data[arr[i]].tele, pcon->data[arr[i]].addr);
}
printf("【总共查找到了%d个联系人!】\n", arr[0]);
if (arr != NULL)
{
free(arr);
}
}
//查找联系人
void searchContact(const Contact* pcon)
{
int input = 0;
int* arr = NULL; //接收一个整形数组,数组中存放查找到的联系人下标
do
{
searchMenu();
printf("请输入你的选择:");
scanf("%d", &input);
switch (input)
{
case 1:
//按姓名查找
arr = searchByName(pcon);
printInfo(pcon, arr);
break;
case 2:
//按年龄查找
arr = searchByAge(pcon);
printInfo(pcon, arr);
break;
case 3:
//按性别查找
arr = searchBySex(pcon);
printInfo(pcon, arr);
break;
case 4:
//按电话查找
arr = searchByTele(pcon);
printInfo(pcon, arr);
break;
case 5:
//按地址查找
arr = searchByAddr(pcon);
printInfo(pcon, arr);
break;
case 0:
//退出查找
printf("退出查找!\n");
break;
default:
printf("输入错误,请重新输入!\n");
}
} while (input);
}
//修改联系人菜单
void modifyMenu()
{
printf("*****************************************\n");
printf("***** 请选择你要按什么条件修改 ******\n");
printf("***** 1.name 2.age ******\n");
printf("***** 3.tele 4.addr ******\n");
printf("***** 0.exit ******\n");
printf("*****************************************\n");
}
//按条件修改联系人
void modifyByCondition(Contact* pcon, int input)
{
//定义一个函数指针数组,将每个查找函数作为数组元素
int* (*fun[5])(Contact*) = { NULL, searchByName, searchByAge, searchByTele, searchByAddr };
int* arr = fun[input](pcon);
if (arr[0] == 1) //没有重复的联系人,直接修改
{
printf("请输入姓名:");
scanf("%s", pcon->data[arr[1]].name);
printf("请输入性别:");
scanf("%s", pcon->data[arr[1]].sex);
printf("请输入年龄:");
scanf("%d", &pcon->data[arr[1]].age);
printf("请输入电话:");
scanf("%s", pcon->data[arr[1]].tele);
printf("请输入地址:");
scanf("%s", pcon->data[arr[1]].addr);
}
else if (arr[0] == 0)
{
printf("没有该联系人!\n");
return;
}
else
{
printf("%-6s %-10s %-8s %-8s %-15s %-15s\n", "编号", "姓名", "性别", "年龄", "电话", "地址"); //打印标题
int i = 0;
for (i = 1; i <= arr[0]; i++)
{
printf("%-6d %-10s %-8s %-8d %-15s %-15s\n", i, pcon->data[arr[i]].name, pcon->data[arr[i]].sex, pcon->data[arr[i]].age, pcon->data[arr[i]].tele, pcon->data[arr[i]].addr);
}
while (1)
{
printf("联系人有多个,请输入你要修改的联系人编号:");
int n = 0;
scanf("%d", &n);
if (n >= 1 && n <= arr[0]) //判断输入是否合法
{
printf("请输入姓名:");
scanf("%s", pcon->data[arr[n]].name);
printf("请输入年龄:");
scanf("%d", &pcon->data[arr[n]].age);
printf("请输入性别:");
scanf("%s", pcon->data[arr[n]].sex);
printf("请输入电话:");
scanf("%s", pcon->data[arr[n]].tele);
printf("请输入地址:");
scanf("%s", pcon->data[arr[n]].addr);
break;
}
else
{
printf("输入错误,请重新输入!\n");
}
}
}
printf("【成功修改联系人!】\n");
}
//修改联系人
void modifyContact(Contact* pcon)
{
int input = 0;
do
{
modifyMenu();
printf("请输入你的选择:");
scanf("%d", &input);
if (input > 0 && input < 5)
{
modifyByCondition(pcon, input);
}
else if (input == 0)
{
printf("退出修改联系人!\n");
break;
}
else
{
printf("输入错误,请重新输入!\n");
}
} while (input);
}
//显示全部联系人
void showContact(const Contact* pcon)
{
printf("%-10s %-8s %-8s %-15s %-15s\n", "姓名", "性别", "年龄", "电话", "地址"); //打印标题
int i = 0;
for (i = 0; i < pcon->sz; i++)
{
printf("%-10s %-8s %-8d %-15s %-15s\n", pcon->data[i].name, pcon->data[i].sex, pcon->data[i].age, pcon->data[i].tele, pcon->data[i].addr);
}
printf("【总共显示了%d个联系人!】\n", pcon->sz);
}
//清空联系人
void emptyContact(Contact* pcon)
{
free(pcon->data);
pcon->data = NULL;
pcon->capacity = DEFAULT_MAX;
pcon->sz = 0;
printf("【成功清空通讯录!】\n");
}
//排序菜单函数
void sortMenu()
{
printf("*****************************************\n");
printf("***** 请选择你要按什么条件排序 ******\n");
printf("***** 1.name ******\n");
printf("***** 2.age ******\n");
printf("***** 0.exit ******\n");
printf("*****************************************\n");
}
//按姓名比较
int sortByName(const void* p1, const void* p2)
{
return (strcmp(((PerInfo*)p1)->name, ((PerInfo*)p2)->name));
}
//按年龄比较
int sortByAge(const void* p1, const void* p2)
{
return (((PerInfo*)p1)->age - ((PerInfo*)p2)->age);
}
//排序联系人
void sortContact(Contact* pcon)
{
int input = 0;
do
{
sortMenu();
printf("请输入你的选择:");
scanf("%d", &input);
switch (input)
{
case 1:
//按姓名排序
qsort(pcon->data, pcon->sz, sizeof(pcon->data[0]), sortByName); //使用库函数qsort
break;
case 2:
//按年龄排序
qsort(pcon->data, pcon->sz, sizeof(pcon->data[0]), sortByAge); //使用库函数qsort
break;
case 0:
printf("退出排序!\n");
break;
default:
printf("输入错误,请重新输入!\n");
}
if (input == 1 && input == 2)
{
printf("【排序成功!】");
showContact(pcon); //将排序结果显示
}
} while (input);
}
//保存数据到文件中
void save(Contact* pcon)
{
//打开文件
FILE* pf = fopen("data.txt", "w");
if (pf == NULL) //如果为NULL说明打开文件失败
{
perror("fopen"); //输出错误信息
return;
}
//写文件
//采用fprintf函数进行写,该函数是功能是:将格式化的数据输出到对应的流中
int i = 0;
for (i = 0; i < pcon->sz; i++)
{
fprintf(pf, "%-10s %-8s %-8d %-15s %-15s\n", pcon->data[i].name, pcon->data[i].sex, pcon->data[i].age, pcon->data[i].tele, pcon->data[i].addr);
}
//关闭文件
fclose(pf);
pf = NULL;
}
四、总结
相对于C语言实现通讯录管理系统(动态开辟内存)这个版本的通讯录仅仅在test.c文件中和contact.c文件中增加了写文件和读文件的操作,在contact.h文件中增加了函数声明。