通讯录优化+文件操作
通讯录优化
相比于上一篇博客的静态通讯录,此版本通讯录增加了动态开辟空间及将通讯录储存进入文本的功能。
即
a. 添加一个函数,在退出通讯录的时候把信息到保存到文件中
b. 添加一个函数,在通讯录打开的时候,可以把文件中的信息加载到通讯录中
具体操作事项见代码和注释
#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include <string.h>
#include <errno.h>
#define NAME_MAX 20
#define SEX_MAX 5
#define TELE_MAX 12
#define ADRR_MAX 30
#define MAX 1000
#define N 1000
//通讯录
//保存1000个人信息:名字、年龄、电话、性别、住址
//功能:1、增加个人信息;2、删除个人信息(指定的);3、查找指定联系人;4、修改指定的联系人信息
//5、显示通讯录中所有人的信息;6、排序所有人的信息;7、退出
//类型的定义放在头文件里
typedef struct PeoInfo
{
char name[NAME_MAX];
int age;
char sex[SEX_MAX];
char tele[TELE_MAX];
char adrr[ADRR_MAX];
} Peoinfo; //描述个人信息的结构体
typedef struct contact
{
Peoinfo *data;
int sz; //表示当前存放的人的信息个数
int max_people; //表示最多存放人数
} contact;
enum option //利用枚举增加程序可读性
{
exit, //0
add, //1
del, //2
search, //3
modify, //4
show, //5
sort, //6
clean, //7
create, //8
enlarge, //9
save //10
};
void menu();
void InitContact(contact *); //初始化通讯录
void AddContact(contact *); //增加个人信息
void ShowContact(contact *); //可视化通讯录
void DelContact(contact *); //删除个人信息
int FindPeoInfoByName(contact *, char *); //寻找个人信息
void SearchContact(contact *); //寻找个人
void ModifyContact(contact *); //修改个人信息
void SortContact(contact *); //按姓名字典序排序
void CleanContact(contact *); //清空通讯录
int EnlargeContact(contact *); //扩容通讯录
void SaveContact(contact *); //将通讯录信息储存
void LoadContact(contact *); //加载已经储存的通讯录
void CheckContact(contact *); //检查通讯录是否已经装载满
int main()
{
int input = 0;
contact con;
InitContact(&con);
LoadContact(&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 clean:
CleanContact(&con);
case create:
InitContact(&con);
case enlarge:
EnlargeContact(&con);
case save:
SaveContact(&con);
case exit:
break;
}
} while (input);
}
void menu()
{
printf("*********************************\n");
printf("*******1.add 2.del*********\n");
printf("*******3.search 4.modify******\n");
printf("*******5.show 6.sort********\n");
printf("*******7.clean 8.create******\n");
printf("*******9.enlarge 10.save*******\n");
printf("*******0.exit **************\n");
printf("*********************************\n");
}
void InitContact(contact *pc)
{
pc->sz = 0;
printf("请输入你需要的通讯录大小:>\n");
scanf("%d", &(pc->max_people));
pc->data = (Peoinfo *)malloc(sizeof(Peoinfo) * pc->max_people);
if (pc->data == NULL)
{
printf("创建失败\n");
}
else
{
printf("创建成功\n");
}
}
void AddContact(contact *pc)
{
CheckContact(pc);
printf("请输入名字:>");
scanf("%s", pc->data[pc->sz].name);
printf("请输入年龄:>");
scanf("%d", &(pc->data[pc->sz].age));
printf("请输入性别:>");
scanf("%s", pc->data[pc->sz].sex);
printf("请输入电话:>");
scanf("%s", pc->data[pc->sz].tele);
printf("请输入地址:>");
scanf("%s", pc->data[pc->sz].adrr);
pc->sz += 1;
printf("添加成功\n");
}
void ShowContact(contact *pc)
{
int i = 0;
printf("%15s\t %5s\t %5s\t %12s\t %20s\n", "名字", "年龄", "性别", "电话", "地址");
for (i = 0; i < pc->sz; i++)
{
printf("%15s\t %d\t %5s\t %12s\t %20s\n", pc->data[i].name, pc->data[i].age, pc->data[i].sex, pc->data[i].tele, pc->data[i].adrr);
}
}
int FindPeoInfoByName(contact *pc, char *name) //封装查找过程
{
int i = 0;
for (i = 0; i < pc->sz; i++)
{
if (pc->data[i].name == name)
{
return i; //如果找到返回地址
}
}
if (i == pc->sz)
{
return -1; //未找到返回-1
}
}
void DelContact(contact *pc)
{
char name[NAME_MAX] = {0};
if (pc->sz == 0)
{
printf("通讯录为空,无法删除\n");
}
else
{
printf("请输入要删除人的名字:>\n");
scanf("%s", name);
int pos = FindPeoInfoByName(pc, name); //找到个人数据
if (pos == -1)
{
printf("被删除的人不存在\n");
}
else
{
//删除
int j = 0;
for (j = pos; j < pc->sz - 1; j++)
{
pc->data[j] = pc->data[j + 1]; //整体前移
}
}
}
}
void SearchContact(contact *pc)
{
char name[NAME_MAX];
scanf("%s", name);
int pos = FindPeoInfoByName(pc, name);
if (pos == -1)
{
printf("查无此人!\n");
}
else
{
printf("%s\t %s\t %s\t %s\t %s\n", "名字", "年龄", "性别", "电话", "地址");
printf("%s\t %d\t %s\t %s\t %s\n", pc->data[pos].name, pc->data[pos].age, pc->data[pos].sex, pc->data[pos].tele, pc->data[pos].adrr);
}
}
void ModifyContact(contact *pc)
{
char name[NAME_MAX];
scanf("%s", name);
int pos = FindPeoInfoByName(pc, name);
if (pos == -1)
{
printf("要修改的人不存在\n");
}
else
{
printf("请输入名字:>");
scanf("%s", pc->data[pos].name);
printf("请输入年龄:>");
scanf("%d", &(pc->data[pos].age));
printf("请输入性别:>");
scanf("%s", pc->data[pos].sex);
printf("请输入电话:>");
scanf("%s", pc->data[pos].tele);
printf("请输入地址:>");
scanf("%s", pc->data[pos].adrr);
printf("修改成功\n");
}
}
void SortContact(contact *pc) //冒泡排序,此处可更改为快速排序以减少时间消耗
{
int i = 0, j = 0;
Peoinfo temp;
for (i = 0; i < pc->sz; i++)
{
int flag = 1;
for (j = 0; j < pc->sz - 1 - i; j++)
{
if (strcmp(pc->data[j].name, pc->data[j + 1].name) > 0)
{
temp = pc->data[j];
pc->data[j] = pc->data[j + 1];
pc->data[j + 1] = temp;
flag = 0;
}
}
if (flag == 1)
{
break;
}
}
}
void CleanContact(contact *pc) //直接释放动态开辟的空间
{
free(pc->data);
pc->sz = 0;
printf("清空完成\n");
}
int EnlargeContact(contact *pc)//更改为int类型防止扩容失败造成程序的崩溃
{
Peoinfo *temp = NULL;
temp = (Peoinfo *)realloc(pc->data, pc->max_people * 2);
if (temp == NULL)
{
printf("扩容失败\n");
return 0;
}
else
{
pc->data = temp;
pc->max_people *= 2;
return 1;
}
}
void SaveContact(contact *pc)
{
FILE *pfOUT = fopen("C:\\Users\\user\\Desktop\\contact.dat", "wb");
if (pfOUT == NULL)
{
printf("%s\n", strerror(errno));
return;
}
int i = 0;
for (i = 0; i < pc->sz; i++)
{
fwrite(pc->data + 1, sizeof(Peoinfo), 1, pfOUT);//循环输入通讯录到文件
}
printf("保存成功\n");
}
void LoadContact(contact *pc)
{
FILE *pfIN = fopen("C:\\Users\\user\\Desktop\\contact.dat", "rb");
if (pfIN == NULL)
{
printf("LoadContact::%s\n", strerror(errno));//优化,便于用户理解错误发生原因
return;
}
Peoinfo temp = {0};
while (fread(&temp, sizeof(Peoinfo), 1, pfIN))//利用循环添加文件中的已有通讯录
{
CheckContact(pc);
pc->data[pc->sz] = temp;
pc->sz++;
}
}
void CheckContact(contact *pc)
{
if (pc->sz == pc->max_people)
{
int temp = EnlargeContact(pc);
while (temp == 0)//利用循环反复扩容直至成功
{
temp = EnlargeContact(pc);
}
printf("扩容成功\n");
}
else
{
return;
}
}
文件操作
文件的打开和关闭(fopen、fclose函数)
fopen函数
fopen函数有三个参数
第一个参数为文件指针
每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息(如文件的名字,文件状态及
文件当前的位置等)。这些信息是保存在一个结构体变量中的。该结构体类型是有系统声明的,取名FILE.
struct _iobuf {
char *_ptr;
int _cnt;
char *_base;
int _flag;
int _file;
int _charbuf;
int _bufsiz;
char *_tmpfname;
};
typedef struct _iobuf FILE;
第二个参数为文件所在路径。此处分为相对路径和绝对路径
第三个参数为操作类型
fclose函数
fclose函数的参数为文件指针,其作用就是关闭文件,在每次打开文件后,我们都需要关闭文件,同时将文件指针置为空指针。
fclose函数的返回值为int类型,若返回值为0意味着成功关闭。
文件读写函数
首先我们需要认识到不同的输入输出流,而文件也是一种输入输出流,即我们可以直接通过文件指针获取或输入,也可从标准输入输出流中获取或输入数据。
fgetc和fputc
获取或输出一个字符,返回值均为读取字符对应的asc码。没调用一次文件指针会向后移动一位
fgets和fputs(文件指针会根据行移动)
fgets函数
fgets的参数为char *string, int n, FILE *stream
即从指定的流 stream 读取一行,并把它存储在 str 所指向的字符串内。当读取 (n-1) 个字符时,或者读取到换行符时,或者到达文件末尾时,它会停止
返回值:
如果成功,该函数返回相同的 str 参数。如果到达文件末尾或者没有读取到任何字符,str 的内容保持不变,并返回一个空指针。如果发生错误,返回一个空指针
fputs函数
int fputs(const char *str, FILE *stream) 把字符串写入到指定的流 stream 中,但不包括空字符。
返回值:
该函数返回一个指针 ,指向重新分配大小的内存。如果请求失败,则返回 NULL
fscanf和fprintf
这两个函数和scanf与printf差别不大,只是添加一个参数,代表流
FILE类型
fread和fwrite
sscanf和sprintf函数
fseek函数
此处需要特别注意的是whence
ftell函数
rewind函数
将文件指针拨回文件起始位置
feof、ferror函数
feof、ferror和判断是否读取结束;feof判断文件是否结束,derror判断文件读取是否失败
需要特别注意的是:
文件缓冲区概念
输入或输出的数据会短暂的存储在缓冲区中,缓冲区存在从而避免操作系统的进程被不停打断。