通讯录优化+文件操作

通讯录优化

相比于上一篇博客的静态通讯录,此版本通讯录增加了动态开辟空间及将通讯录储存进入文本的功能。

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判断文件读取是否失败
需要特别注意的是:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

文件缓冲区概念

输入或输出的数据会短暂的存储在缓冲区中,缓冲区存在从而避免操作系统的进程被不停打断。
在这里插入图片描述
在这里插入图片描述

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值