本通讯录的最终目标:
1. 可以动态存放联系人的信息
2. 人的信息:名字,性别,年龄,电话,住址
3. 增加联系人
4. 删除联系人
5. 查找联系人
6. 修改联系人
7. 排序(名字 / 年龄)
8. 清除所有联系人
9. 以文件的形式:程序结束后会保存数据;运行的时候可以对文件进行加载
之前介绍了初级版通讯录,后来又改进为动态通讯录,提高了代码的运行效率。那么就有一个问题,能不能实现一个即使是程序退出也能将程序运行时输入的数据保存下来,在下一次运行程序的时候可以把之前存下来的数据加载出来,所以这里就运用了文件操作的内容,下面代码就来实现通讯录版本三之文件版本通讯录。
本程序是对之前通讯录进行修改,所以大体思路并未改变,下面对程序修改之处进行介绍:
contact.c:
#define _CRT_SECURE_NO_WARNINGS 1
#include "file_dy_contact.h"
void check_capacity(Contact* con)
{
assert(con);
if (con->sz == con->capacity)
{
//增加容量
PeoInfo* tmp = (PeoInfo*)realloc(con->data, (con->capacity + 2) * sizeof(PeoInfo));
if (tmp != NULL)
{
con->data = tmp;
con->capacity += 2;
//printf("增容成功!\n");
}
else
{
printf("check_capacity()::%s\n", strerror(errno));
}
}
}
void LoadContact(Contact* con)
{
assert(con);
FILE* pf = fopen("contact.txt", "rb");
if (pf == NULL)
{
if (strcmp(strerror(errno), "No such file or directory") == 0)
{
printf("创建新的通讯录成功!\n请操作:\n");
return;
}
else
{
printf("LoadContact::%s\n", strerror(errno));
return;
}
}
else
{
printf("请继续上次通讯录操作:\n");
}
PeoInfo buf = { 0 };
while (fread(&buf, sizeof(PeoInfo), 1, pf))
{
check_capacity(con);
con->data[con->sz] = buf;
con->sz++;
}
fclose(pf);
pf = NULL;
}
void InitContact(Contact* con)
{
assert(con);
con->sz = 0;
PeoInfo* tmp = (PeoInfo*)malloc(DEFAULT_SZ * sizeof(PeoInfo));
if (tmp != NULL)
{
con->data = tmp;
}
else
{
printf("InitContact()::%s\n", strerror(errno));
return;
}
con->capacity = DEFAULT_SZ;
LoadContact(con);
}
void AddContact(Contact* con)
{
assert(con);
check_capacity(con);
//输入联系人
printf("请输入名字:>");
scanf("%s", con->data[con->sz].name);
printf("请输入性别:>");
scanf("%s", con->data[con->sz].sex);
printf("请输入年龄:>");
scanf("%d", &(con->data[con->sz].age));
printf("请输入电话号码:>");
scanf("%s", con->data[con->sz].tele);
printf("请输入住址:>");
scanf("%s", con->data[con->sz].addr);
con->sz++;
printf("添加联系人成功!\n");
}
int FindContact(const Contact* con, char name[])
{
assert(con && name);
int i = 0;
for (i = 0; i < con->sz; i++)
{
if (strcmp(con->data[i].name, name) == 0)
{
return i;
}
}
return -1;
}
void ShowContact(const Contact* con)
{
assert(con);
printf("%-10s\t%-5s\t%-5s\t%-13s\t%-20s\n", "名字", "性别", "年龄", "电话", "地址");
int i = 0;
for (i = 0; i < con->sz; i++)
{
printf("%-10s\t%-5s\t%-5d\t%-13s\t%-20s\n",
con->data[i].name, con->data[i].sex, con->data[i].age, con->data[i].tele, con->data[i].addr);
}
}
void SearchContact(const Contact* con)
{
assert(con);
char searchname[NAME_MAX] = { 0 };
if (con->sz == 0)
{
printf("通讯录为空,无法查找\n");
return;
}
printf("请输入查找人的名字:>");
scanf("%s", searchname);
int ret = FindContact(con, searchname);
if (ret == -1)
{
printf("未能找到该联系人\n");
return;
}
else
{
printf("找到了:>\n");
printf("%-10s\t%-5s\t%-5s\t%-13s\t%-20s\n", "名字", "性别", "年龄", "电话", "地址");
printf("%-10s\t%-5s\t%-5d\t%-13s\t%-20s\n",
con->data[ret].name, con->data[ret].sex, con->data[ret].age, con->data[ret].tele, con->data[ret].addr);
}
}
void DelContact(Contact* con)
{
assert(con);
char delname[NAME_MAX] = { 0 };
if (con->sz == 0)
{
printf("通讯录为空,无法删除\n");
return;
}
printf("请输入删除人名字:>");
scanf("%s", delname);
int ret = FindContact(con, delname);
if (ret == -1)
{
printf("通讯录中暂无此人,无法删除\n");
return;
}
else
{
int i = 0;
for (i = ret; i < con->sz - 1; i++)
{
con->data[i] = con->data[i + 1];
}
con->sz--;
printf("删除联系人成功!\n");
}
}
void ModifyContact(Contact* con)
{
assert(con);
char modifyname[NAME_MAX] = { 0 };
char mod[5] = { 0 };
char modform[50] = { 0 };
if (con->sz == 0)
{
printf("通讯录为空,无法修改\n");
return;
}
printf("请输入修改人的名字:>");
scanf("%s", modifyname);
int ret = FindContact(con, modifyname);
if (ret == -1)
{
printf("通讯录中暂无此人,无法修改\n");
return;
}
else
{
printf("请选择修改的选项(名字/性别/年龄/电话/地址):>");
scanf("%s", mod);
if (strcmp(mod, "名字") == 0)
{
printf("请输入修改后的名字:>");
scanf("%s", modform);
strcpy(con->data[ret].name, modform);
printf("修改名字成功!\n");
}
else if (strcmp(mod, "性别") == 0)
{
printf("请输入修改后的性别:>");
scanf("%s", modform);
strcpy(con->data[ret].sex, modform);
printf("修改性别成功!\n");
}
else if (strcmp(mod, "电话") == 0)
{
printf("请输入修改后的电话:>");
scanf("%s", modform);
strcpy(con->data[ret].tele, modform);
printf("修改电话成功!\n");
}
else if (strcmp(mod, "地址") == 0)
{
printf("请输入修改后的地址:>");
scanf("%s", modform);
strcpy(con->data[ret].addr, modform);
printf("修改地址成功!\n");
}
else if (strcmp(mod, "年龄") == 0)
{
printf("请输入修改后的年龄:>");
scanf("%s", modform);
int newage = atoi(modform);
con->data[ret].age = newage;
printf("修改年龄成功!\n");
}
else
{
printf("选择错误\n");
}
}
}
int cmp_people_by_name(const void* e1, const void* e2)
{
return strcmp(((PeoInfo*)e1)->name, ((PeoInfo*)e2)->name);
}
int cmp_people_by_age1(const void* e1, const void* e2)
{
return ((PeoInfo*)e1)->age - ((PeoInfo*)e2)->age;
}
int cmp_people_by_age2(const void* e1, const void* e2)
{
return ((PeoInfo*)e2)->age - ((PeoInfo*)e1)->age;
}
void SortContact(Contact* con)
{
assert(con);
char sort[5] = { 0 };
char sortway[5] = { 0 };
if (con->sz == 0)
{
printf("通讯录为空,无法排序\n");
return;
}
else
{
printf("请选择排序方式(名字/年龄):>");
scanf("%s", sort);
if (strcmp(sort, "名字") == 0)
{
qsort(con->data, con->sz, sizeof(con->data[0]), cmp_people_by_name);
printf("排序成功!\n");
}
else if (strcmp(sort, "年龄") == 0)
{
printf("请选择递增or递减:>");
scanf("%s", sortway);
if (strcmp(sortway, "递增") == 0)
{
qsort(con->data, con->sz, sizeof(con->data[0]), cmp_people_by_age1);
printf("排序成功!\n");
}
else if (strcmp(sortway, "递减") == 0)
{
qsort(con->data, con->sz, sizeof(con->data[0]), cmp_people_by_age2);
printf("排序成功!\n");
}
else
{
printf("选择错误\n");
}
}
else
{
printf("选择错误\n");
}
}
}
void ClearContact(Contact* con)
{
assert(con);
if (con->sz == 0)
{
printf("通讯录为空,无需清除\n");
return;
}
else
{
memset(con->data, 0, sizeof(con->data));
con->sz = 0;
printf("清除所有联系人成功!\n");
}
}
void SaveContact(Contact* con)
{
assert(con);
//打开文件
FILE* pf = fopen("contact.txt", "wb");
if (pf == NULL)
{
printf("SaveContact::%s\n", strerror(errno));
return;
}
//写文件
int i = 0;
for (i = 0; i < con->sz; i++)
{
fwrite(con->data + i, sizeof(PeoInfo), 1, pf);
}
//关闭文件
fclose(pf);
pf = NULL;
}
void DestroyContact(Contact* con)
{
free(con->data);
con->data = NULL;
con->capacity = 0;
con->sz = 0;
}
这里的LoadContact函数是对上一次程序运行是的数据进行加载,如果是第一次运行这个程序,则比较报错原因是否是没有当前文件,如果是才会报错,其余情况都可正常运行;如果不是第一次运行这个程序,则用frread二进制读取文件来加载之前的数据。保存数据则是在代码后面的SaveContact函数来实现,用fwrite二进制写入文件来将数据存起来。
test.c:
#define _CRT_SECURE_NO_WARNINGS 1
//通讯录:
//1. 可以动态存放联系人的信息
//2. 人的信息:名字,性别,年龄,电话,住址
//3. 增加联系人
//4. 删除联系人
//5. 查找联系人
//6. 修改联系人
//7. 排序(名字 / 年龄)
//8. 清除所有联系人
//9. 以文件的形式:程序结束后会保存数据;运行的时候可以对文件进行加载
#include "file_dy_contact.h"
enum Oprion
{
EXIT,
ADD,
DEL,
SEARCH,
MODIFY,
SORT,
SHOW,
CLEAR
};
void menu()
{
printf("***********************************\n");
printf("******** 1. add 2. del ******\n");
printf("******** 3. search 4. modify******\n");
printf("******** 5. sort 6. show ******\n");
printf("******** 7. clear 0. exit ******\n");
printf("***********************************\n");
}
int main()
{
int input = 0;
Contact con = { 0 }; //通讯录
//初始化通讯录
InitContact(&con);
do
{
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case ADD:
AddContact(&con);
break;
case DEL:
DelContact(&con);
break;
case SEARCH:
SearchContact(&con);
break;
case MODIFY:
ModifyContact(&con);
break;
case SORT:
SortContact(&con);
break;
case SHOW:
ShowContact(&con);
break;
case CLEAR:
ClearContact(&con);
break;
case EXIT:
SaveContact(&con);
DestroyContact(&con);
printf("退出通讯录\n");
break;
default:
printf("选择错误\n");
break;
}
} while (input);
return 0;
}
contact.h:
#pragma once
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <stdlib.h>
#include <errno.h>
#define NAME_MAX 20
#define SEX_MAX 5
#define TELE_MAX 15
#define ADDR_MAX 30
#define DEFAULT_SZ 3
typedef struct PeoInfo
{
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 sz; //通讯录中有效信息个数
int capacity; //记录当前通讯录最大容量
}Contact;
//初始化通讯录
void InitContact(Contact* con);
//加载文件
void LoadContact(Contact* con);
//增添联系人
void AddContact(Contact* con);
//删除联系人
void DelContact(Contact* con);
//查找联系人
void SearchContact(const Contact* con);
//修改联系人
void ModifyContact(Contact* con);
//排序联系人
void SortContact(Contact* con);
//打印联系人
void ShowContact(const Contact* con);
//清除所有联系人
void ClearContact(Contact* con);
//保存通讯录到文件
void SaveContact(Contact* con);
//销毁通讯录
void DestroyContact(Contact* con);
整个代码相较于动态通讯录来说,只是多了数据加载和数据保存这两部分,到这里,三个版本的通讯录就完结了。
整个文件版通讯录的完整代码放在这里面,有兴趣可以看看: