重庆分公司,新征程启航
为企业提供网站建设、域名注册、服务器等服务
通讯录的最新版本来啦,请注意查收~~~
创新互联服务项目包括樊城网站建设、樊城网站制作、樊城网页制作以及樊城网络营销策划等。多年来,我们专注于互联网行业,利用自身积累的技术优势、行业经验、深度合作伙伴关系等,向广大中小型企业、政府机构等提供互联网行业的解决方案,樊城网站推广取得了明显的社会效益与经济效益。目前,我们服务的客户以成都为中心已经辐射到樊城省份的部分城市,未来相信会继续扩大服务区域并继续获得客户的支持与信任!在手机中的通讯录,与我们之前的两个通讯录版本相比,大的区别是每次关掉程序或者关机重启,联系人的信息不会像在内存中的程序一样丢失,而是保存在了本地的硬盘里,这一次,我们就使用C语言中的文件功能对通讯录进行改进。
总结一下,需要完成的功能为:在退出时保存信息到文件;打开时加载文件中的信息到程序。
目录
一、代码优化
1.退出时保存信息
(1)创建函数
(2)保存信息
(3)关闭文件
2.打开程序时加载文件中的信息
(1)创建读取函数
(2)读取信息
(3)关闭文件
三、文件代码
1.contact.h
2.contact.c
3.test.c
四、注意事项
我们创建SaveContact函数用来将信息保存至文件中,将这个函数放在switch语句中的case选项下,先存放到文件中,再销毁程序中的通讯录。
// test.c
...
case EXIT:
SaveContact(&contact);
DestoryContact(&contact);
break;
...
(1)创建函数首先需要先打开文件,采用写入的方式,我们定义FILE类型指针,指向使用fopen打开的文件,并判断文件指针是否为空,即是否打开文件失败,失败就提示错误信息并退出函数。
(2)保存信息当打开文件成功后,就可以进行写入了,我们使用for循环进行逐个写入,使用fwrite写入文件,信息源为联系人列表,每次写入一个联系人信息所占的字节数,每次写入一个联系人信息,写入文件指针类型所指向的文件中。
(3)关闭文件最后就是关闭文件了,使用fclose关闭文件,并将指针置空。
每次使用文件指针后切记要关闭文件,并将指针置空,防止内存访问异常!
函数代码如下:
// contact.c
void SaveContact(Contact* pc)
{
// 打开文件
FILE* pf = fopen("contact.dat", "w");
if (pf == NULL)
{
perror("fopen");
return;
}
// 写文件
int i = 0;
for (i = 0; i< pc->sz; i++)
{
// 要写入的信息来源为联系人列表;每次写入一个联系人的信息大小;
// 每次写入一个联系人信息;写入pf所指向的文件中
fwrite(pc->data+i, sizeof(PeoInfo), 1,pf);
}
// 关闭文件
fclose(pf);
pf = NULL;
}
2.打开程序时加载文件中的信息加载文件就是指读取文件中的内容到程序中,我们创建LoadContact函数用来读取文件中的信息。
需要注意的是,我们应该在程序初始化时读取文件的内容,而不是到用户选择功能时再进行读取。将读取文件的函数放在初始化函数中。
// contact.c
void InitContact(Contact* pc)
{
...
LoadContact(pc);
}
(1)创建读取函数a.读取文件
使用FILE类型指针指向使用fopen打开存放信息的文件"contact.dat",需要注意的是,这里应当使用"r"对文件进行读取。要记得进行判断是否打开成功~
(2)读取信息使用fread对信息进行读取,创建一个临时的联系人列表temp用来存放读取得到的信息,并对temp进行判断:
若读取为0表示文件中没有信息;
若读取成功就将读取的信息存放到程序中创建的通讯录中,这时候文件中的联系人数量可能会大于我们设置的默认数量,需要判断是否需要对容量进行增加,我们使用CheckCapicaty函数进行判断。
// contact.c
void LoadContact(Contact* pc)
{
...
CheckCapacity(pc);
...
}
具体的CheckCapacity函数在动态版本中已有写明,可移步查看。
我们将临时列表中的信息存放到程序中的通讯录中,使用while函数进行判断,当读取的信息不为空时,将联系人信息逐条转存到联系人列表中,为空时就退出程序。
(3)关闭文件在读取完成后,文件中的联系人信息已被存至程序中的通讯录中,直到退出通讯录时才需要再次打开文件进行写入,故在完成读取后需要关闭文件。
函数代码如下:
// contact.c
void LoadContact(Contact* pc)
{
FILE* pf = fopen("contact.dat", "r");
if (pf == NULL)
{
perror("LoadContact");
return;
}
// 读文件
PeoInfo temp = { 0 };
while (fread(&temp, sizeof(PeoInfo), 1, pf))
{
// 是否需要增容
CheckCapacity(pc);
pc->data[pc->sz] = temp;
pc->sz++;
}
// 关闭文件
fclose(pf);
pf = NULL;
}
三、文件代码
1.contact.h#define _CRT_SECURE_NO_WARNINGS 1
#pragma warning(disable:6031)
// 相关函数的声明
// 一些类型的定义
#include#include#include#define MAX_NAME 20
#define MAX_SEX 10
#define MAX_TEL 15
#define MAX_ADDR 30
#define MAX 1000
#define DEFAULT_SZ 3
#define INC_SZ 2
// 联系人的存放
// 使用typedef 定义结构体类型,方便调用
typedef struct PeoInfo
{
char name[MAX_NAME]; // 姓名
char sex[MAX_SEX]; // 性别
int age; // 年龄
char tel[MAX_TEL]; // 电话
char addr[MAX_ADDR]; // 地址
}PeoInfo;
// 通讯录
typedef struct Contact
{
PeoInfo* data; // 指向动态申请的空间,用来存放联系人的信息
int sz; // 通讯录中具体的联系人数量
int capacity; // 记录当前通讯录的大容量
}Contact;
// 初始化通讯录
void InitContact(Contact* pc);
// 增加联系人
void AddContact(Contact* pc);
// 打印联系人
void PrintContact(const Contact* pc);
// 删除联系人
void DelContact(Contact* pc);
// 查找联系人
void SearchContact(Contact* pc);
// 修改联系人
void ModifyContact(Contact* pc);
// 排序联系人
void SortContact(Contact* pc);
// 销毁通讯录
void DestorContact(Contact* pc);
// 保存通讯录信息
void SaveContact(Contact* pc);
2.contact.c#define _CRT_SECURE_NO_WARNINGS 1
#pragma warning(disable:6031)
// 具体的功能函数的实现
#include"contact.h"
// 保存信息到文件中
void SaveContact(Contact* pc)
{
// 打开文件
FILE* pf = fopen("contact.dat", "w");
if (pf == NULL)
{
perror("SaveContact");
return;
}
// 写文件
int i = 0;
for (i = 0; i< pc->sz; i++)
{
// 要写入的信息来源为联系人列表;每次写入一个联系人的信息大小;
// 每次写入一个联系人信息;写入pf所指向的文件中
fwrite(pc->data + i, sizeof(PeoInfo), 1, pf);
}
// 关闭文件
fclose(pf);
pf = NULL;
}
// 判断是否需要增容
void CheckCapacity(Contact* pc)
{
if (pc->sz == pc->capacity)
{
PeoInfo* ptr = (PeoInfo*)realloc(pc->data, (pc->capacity + INC_SZ) * sizeof(PeoInfo));
if (ptr != NULL)
{
pc->data = ptr;
pc->capacity += INC_SZ;
printf("增容成功\n");
}
else
{
perror("AddContact");
printf("增加联系人失败\n");
return;
}
}
}
void LoadContact(Contact* pc)
{
FILE* pf = fopen("contact.dat", "r");
if (pf == NULL)
{
// 提示错误并返回
perror("LoadContact");
return;
}
// 读取文件
PeoInfo temp = { 0 };
while (fread(&temp, sizeof(PeoInfo), 1, pf))
{
// 是否需要增容
CheckCapacity(pc);
pc->data[pc->sz] = temp;
pc->sz++;
}
// 关闭文件
fclose(pf);
pf = NULL;
}
// 文件版本-初始化通讯录
void InitContact(Contact* pc)
{
pc->data = (PeoInfo*)malloc(DEFAULT_SZ * sizeof(PeoInfo));
if (pc->data == NULL)
{
perror("InitContact");
return;
}
pc->sz = 0;
pc->capacity = DEFAULT_SZ;
// 加载文件
LoadContact(pc);
}
// 动态版本——增加联系人
void AddContact(Contact* pc)
{
// 判断空间是否足够
CheckCapacity(pc);
// 增加联系人信息
printf("请输入姓名:>");
scanf("%s", pc->data[pc->sz].name);
printf("请输入性别:>");
scanf("%s", pc->data[pc->sz].sex);
printf("请输入年龄:>");
// 变量需要使用&
scanf("%d", &pc->data[pc->sz].age);
printf("请输入电话:>");
scanf("%s", pc->data[pc->sz].tel);
printf("请输入地址:>");
scanf("%s", pc->data[pc->sz].addr);
// 使联系人数量+1
pc->sz++;
printf("增加成功\n");
}
// 动态版本——销毁通讯录
void DestorContact(Contact* pc)
{
free(pc->data);
pc->data = NULL;
pc->sz = 0;
pc->capacity = 0;
}
// 打印联系人——加上const确保在函数内部联系人信息不会被改变
void PrintContact(const Contact* pc)
{
if (pc->sz == 0)
{
printf("通讯录中无联系人信息,无需打印\n");
return;
}
// 打印表头
printf("%-10s\t%-10s\t%-10s\t%-10s\t%-10s\n", "姓名", "性别", "年龄", "电话", "地址");
// 打印联系人信息
int i = 0;
for (i = 0; i< pc->sz; i++)
{
printf("%-10s\t%-10s\t%-10d\t%-10s\t%-10s\n",
pc->data[i].name,
pc->data[i].sex,
pc->data[i].age,
pc->data[i].tel,
pc->data[i].addr
);
}
printf("打印完成\n");
}
// 通过姓名查找联系人
// 创建查找函数,方便被删除修改和查找时调用
// 使用static 使得此函数只能在本函数内部被调用和查看
static int FindName(const Contact* pc, const char input_name[])
{
int i = 0;
for (i = 0; i< pc->sz; i++)
{
// strcmp的结果为0时是找到了对应的姓名
if (strcmp(pc->data[i].name, input_name) == 0)
{
// 则返回对应的联系人信息下标
return i;
}
}
// 找不到
return -1;
}
// 删除联系人
void DelContact(Contact* pc)
{
// 判断通讯录是否为空
if (pc->sz == 0)
{
printf("通讯录为空,无需删除\n");
return;
}
// 查找要删除的人
char input_name[MAX_NAME];
printf("请输入要删除人的姓名:>");
scanf("%s", input_name);
// 设定ret判断返回结果
int ret = FindName(pc, input_name);
// 当查找失败时
if (ret == -1)
{
printf("要删除的人不存在\n");
return;
}
// 删除后,将删除联系人之后的联系人信息的下标-1
int i = 0;
for (i = ret; i< pc->sz; i++)
{
pc->data[i] = pc->data[i + 1];
}
// 删除成功后使联系人数量减1
pc->sz--;
printf("删除成功\n");
}
// 查找联系人
void SearchContact(Contact* pc)
{
char find_name[MAX_NAME] = { 0 };
printf("请输入查找的联系人姓名:>");
scanf("%s", find_name);
int ret = FindName(pc, find_name);
if (ret == -1)
{
printf("查无此人\n");
return;
}
else
{
printf("%-10s\t%-10s\t%-10s\t%-10s\t%-10s\n", "姓名", "性别", "年龄", "电话", "地址");
printf("%-10s\t%-10s\t%-10d\t%-10s\t%-10s\n",
pc->data[ret].name,
pc->data[ret].sex,
pc->data[ret].age,
pc->data[ret].tel,
pc->data[ret].addr
);
printf("查找完毕\n");
}
}
// 修改联系人
void ModifyContact(Contact* pc)
{
char input_name[MAX_NAME] = { 0 };
printf("请输入要修改的联系人的姓名:>");
scanf("%s", input_name);
int ret = FindName(pc, input_name);
if (ret == -1)
{
printf("查无此人,无法修改\n");
return;
}
printf("请输入姓名:>");
scanf("%s", pc->data[ret].name);
printf("请输入性别:>");
scanf("%s", pc->data[ret].sex);
printf("请输入年龄:>");
// 变量需要使用&
scanf("%d", &pc->data[ret].age);
printf("请输入电话:>");
scanf("%s", pc->data[ret].tel);
printf("请输入地址:>");
scanf("%s", pc->data[ret].addr);
printf("修改完毕\n");
}
// 创建比较函数
int cmp_name(const void* p1, const void* p2)
{
return strcmp(((PeoInfo*)p1)->name, ((PeoInfo*)p2)->name);
}
// 排序联系人
void SortContact(Contact* pc)
{
// 判断通讯录是否为空
if (pc->sz == 0)
{
printf("通讯录为空,无需排序\n");
return;
}
// 以姓名为排序依据进行排序
// 使用qsort函数进行排序——调用了创建的cmp_name()
// 待排序列地址、待排关键字个数、关键字大小、比较函数地址
qsort(pc->data, pc->sz, sizeof(PeoInfo), cmp_name);
printf("排序成功\n");
}
3.test.c#define _CRT_SECURE_NO_WARNINGS 1
#pragma warning(disable:6031)
// 测试文件
// 文件版本
// 当退出通讯录时,将联系人信息保存到文件中
// 当打开通讯录时,加载文件中的联系人信息到程序中
//
#include"contact.h"
void menu()
{
printf("*******************\n");
printf("**** 1.add ****\n"); // 增加联系人信息
printf("**** 2.del ****\n"); // 删除联系人信息
printf("**** 3.serach ****\n"); // 查找联系人信息
printf("**** 4.modify ****\n"); // 修改联系人信息
printf("**** 5.sort ****\n"); // 排序联系人信息
printf("**** 6.print ****\n"); // 打印联系人信息
printf("**** 0.exit ****\n"); // 退出通讯录
printf("*******************\n");
}
// 使用枚举,使得switch选择时,更加明了
enum Menu
{
EXIT, // 默认为 0
ADD, // 1
DEL,
SEARCH,
MODIFY,
SORT,
PRINT
};
int main() // 在静态模式下会因为数据过多报警告
{
int input = 0;
// 创建通讯录
Contact contact;
// 初始化通讯录
InitContact(&contact);
do
{
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case ADD:
AddContact(&contact);
break;
case DEL:
DelContact(&contact);
break;
case SEARCH:
SearchContact(&contact);
break;
case MODIFY:
ModifyContact(&contact);
break;
case SORT:
SortContact(&contact);
break;
case PRINT:
PrintContact(&contact);
break;
case EXIT:
// 保存信息到文件
SaveContact(&contact);
DestorContact(&contact);
printf("程序结束\n");
break;
default:
printf("选择错误,请重新选择:\n");
}
} while (input);
return 0;
}
四、注意事项1.在之前两个版本中,有少许代码无用,不影响功能的执行,在当前版本进行了适当优化。
2.目前为止的三个版本分别对应不同的C语言内容,可根据自身学习进度进行实现,做到先理解,再独立完成。不要没有学习对应的内容就进行编写,一口吃不成胖子,学习急不得。
3.通讯录的编写告一段落,日后若有新的想法或者更新,将会编写相关的文章用来讲述。
4.gitee链接
shiwuqing-Contact-3
你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧