编写通讯录(文件版)

本文为代码实例,下面直接开搞!

目标:编写通讯录

实现功能为:1.增加联系人到文件中

2.将文件中的联系人删除

3.查找文件中的联系人

4.修改联系人的信息

5.将联系人顺序进行排序

序:通讯录头文件为concat.h

存放通讯录所需函数的文件为concat.c

测试函数运行的文件为test.c

一、用户交互设计

#include"contact.h"

//选项
enum inputs
{
    exits,
    add,
    delete,
    serch,
    charge,
    display,
    c_sort
}input;
//菜单
void menu()
{
    printf("****************************\n");
    printf("*****1.增加        2.删除*****\n");
    printf("*****3.查找        4.修改*****\n");
    printf("*****5.显示        6.排序*****\n");
    printf("*****0.退出               *****\n");
    printf("****************************\n");
}


//进行通讯录主体的基本运行
int main()
{
    int input = 0;
    concat con;                    //创建通讯录
    Initconcat(&con);            //初始化通讯录中内容为0
    do
    {
        menu();
        printf("请选择:>");
        scanf("%d", &input);
        switch (input)
        {
        case add://增加
            Add(&con);
            break;
        case delete://删除
            Delete(&con);
            break;
        case serch://查找
            Serch(&con);
            break;
        case charge://修改
            Charge(&con);
            break;
        case display://显示
            Display(&con);
            break;
        case c_sort://排序
            C_sort(&con);
            break;
        case exits://退出
            save(&con);
            printf("欢迎下次使用\n");
            break;
        default:
            printf("选择有误,请重新选择\n");
            break;
        }
    } while (input);
}

在用户交互设计中,采用do-while循环直至用户选择退出选项退出通讯录,除此之外将继续进行运行,

同时在case选项中使用了联合体类型作为选项的值,这对我们阅读源码时是有帮助的

二、针对通讯录主体进行编写

1、在编写通讯录时,首先就是要有一个针对存储每个人信息的变量,在创建完一个人的变量,接下来分析,既然我们要写文件版,那么很关键的一个点就是,将文件中的信息读取到内存中,同理我们创建一个结构体变量来存储从文件中读取出的信息,除人员信息外,我们还可以获得总人数,从而更方便我们动态开辟存储空间。

#include<stdio.h>
#include<string.h>
#include<assert.h>
#include<stdlib.h>

#define NAME_MAX 7        //姓名最大长度
#define PHONE_MAX 11      //电话号码最大长度
#define PEOPLE_NUM 10     
#define DEFAULT_SIZE 3    //针对初始化时,动态开辟空间大小的设置
#define IN_SZ 2           //针对后续动态空间不足时,每次增容大小的设置

//存放人员信息的结构体变量
typedef struct people
{
    char name[NAME_MAX];
    int age;
    char sex[5];
    char phone[PHONE_MAX];
}people;

//存放通讯录人员信息及个数
typedef struct concat
{
    people* date;//通过指针控制人员的创建与删除,不在通过静态的数组来控制
    int size;//人员个数
    int capacity;//当前最大容量
}concat;

通过上述准备,我们就有访问文件的工具了,接下来就是针对通讯录各个功能的完善

  1. 首先我们实现初始化功能

初始化功能,其目的是将文件中的数据读取至动态开辟的内存中,为此我们需要几个文件操作的函数,来帮助我们初始化通讯录,

在C语言标准中,设有FILE类型的指针即文件指针,通过文件指针来访问文件内的内容,以此来达到读取文件内容的目的,想要成功读取文件的内容需要使用fopen函数将文件的内容托管给文件指针,以某种方式,这里的读取方式进行简单列出

//初始化通讯录空间,同时通讯录空间应当是可以动态开辟的,
void Initconcat(concat* con)
{
    assert(con);    //检查通讯录变量创建是否成功
    con->size = 0;
    people* ptr = (people*)calloc(DEFAULT_SIZE,sizeof(people));//先申请能够存放DEFAULT_SIZE个人员信息的空间,并将内容都初始化为0;
    if(ptr==NULL)//如果空间开辟失败,则报错
    {
        perror("通讯录空间初始化失败");
    }
    con->date = ptr;        //用通讯录中存放人员信息的指针接管初始化的空间
    con->capacity = DEFAULT_SIZE;//将临时通讯录的最大值初始化为DEFAULT_SIZE

    //加载信息到通讯录
    LoadConcat(con);
}
—————————————————————————————————————————————————————————————————————————————————————————————
//加载信息到通讯录
void LoadConcat(concat* con)
{
    assert(con);
    //打开文件
    FILE* fp = fopen("concat.txt","r");//该函数在读取失败时会返回空指针表示错误
    if (fp == EOF)
    {
        //读取失败
        perror("LoadConcat");//C 库函数 void perror(const char *str) 把一个描述性错误消息输出到标
                             // 准错误 stderr。首先输出字符串 str,后跟一个冒号,然后是一个空格
    }
    else
    {
        people tmp = { 0 };//创建用于交换的人员变量
        int i = 0;
        while (fread(&tmp, sizeof(people), 1, fp))//每次从文件读出一个人员大小的内容
        {
            //首先增容一下
            check_capcity(con);//增容函数,防止在传信息时出现空间不够用的情况
            con->date[i] = tmp;//通过变量i,来控制通讯录中人员信息数组的指针移动
            con->size++;        //通讯录人员数量+1
            i++;
        }
        fclose(fp);//关闭文件
        fp = NULL;//文件指针置空,防止野指针出现
    }
}

————————————————————————————————————————————————————————————————————————————————————————————
//对通讯录空间进行扩容
void check_capcity(concat*con)
{
    assert(con);
    if (con->capacity == con->size)//当目前通讯录满了之后扩容
    {
        people* ptr = (people*)realloc(con->date,(con->capacity+IN_SZ)*sizeof(people));
        if (ptr == NULL)
        {
            perror("check_capcity::realloc");
        }
        else
        {
            con->date = ptr;
            con->capacity += IN_SZ;
            printf("增容成功\n");
        }
    }
}
  1. 在完成初始化的一系列操作之后,我们已经可以成功将文件中的数据读取至内存中,当然我们在功能介绍的时候也写了,是需要进行其他操作的那么在操作完成之后我们是需要将内存中的数据保存至文件中,那么我们就写一个保存函数,将内存中的数据在写入文件中

//保存函数
void save(concat* con)
{
    //打开文件
    FILE* fp = fopen("concat.txt", "r+");
    if (fp == EOF)
    {
        //读取失败
        perror("save::fopen");
    }
    else
    {
        for (int i = 0; i < con->size; i++)
        {
            fwrite(con->date+i, sizeof(people), 1, fp);
        }
        printf("保存成功\n");
    }
    fclose(fp);
    fp = NULL;
}

C 库函数 size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)

ptr 所指向的数组中的数据写入到给定流 stream

  • ptr -- 这是指向要被写入的元素数组的指针。

  • size -- 这是要被写入的每个元素的大小,以字节为单位。

  • nmemb -- 这是元素的个数,每个元素的大小为 size 字节。

  • stream -- 这是指向 FILE 对象的指针,该 FILE 对象指定了一个输出流。

其实fread函数与fwrite函数类似,只是ptr指向的时要存储文件内容的指针,而stream是指向FILE指定的一个输入流(即文件)

  1. 在上述读取以及保存都完成后,我们针对通讯录文件版的关键部分就已经完成了,下面就是针对各个模块的编写了,

//查找通讯录成员
int find_name(const concat* con)
{
    assert(con);
    char S_name[NAME_MAX] = "0";
    scanf("%s", S_name);
    int i = 0;
    for (i = 0; i < con->size; i++)
    {
        if (strcmp(con->date[i].name, S_name) == 0)
        {
            return i;//找到返回位置
        }
    }
    return -1;//找不到返回-1
}



//增加通讯录成员
void Add(concat* con)
{
    assert(con);
    if (con->size < con->capacity)
    {
        printf("请输入姓名:>");
        scanf("%s", con->date[con->size].name);
        printf("请输入年龄:>");
        scanf("%d", &con->date[con->size].age);
        printf("请输入性别:>");
        scanf("%s", &con->date[con->size].sex);
        printf("请输入电话:>");
        scanf("%s", con->date[con->size].phone);
        con->size += 1;
    }
    else
    {
        //扩容
        check_capcity(con);
        return;
    }
}

//删除通讯录成员
void Delete(concat* con)
{
    assert(con);
    printf("请输入要删除人员姓名:");
    if (con->size == 0)
    {
        printf("通讯录无内容,删除失败\n");
        return;
    }
    int ret = find_name(con);
    if (ret != -1)
    {
        int i = 0;
        //删除,用后面的元素将前面的元素覆盖掉
        for (i = ret; i < con->size; i++)
        {
            con->date[i] = con->date[i + 1];
        }
        
        con->size--;
        printf("删除成功\n");
    }
    else
    {
        printf("要删除的联系人不存在\n");
    }
}

//查找通讯录成员
void Serch(const concat* con)
{
    assert(con);
    printf("请输入要查找人员姓名:");
    int ret = find_name(con);
    if (ret != -1)
    {
        printf("姓名:>");
        printf("%s\n", con->date[ret].name);
        printf("年龄:>");
        printf("%d\n", con->date[ret].age);
        printf("性别:>");
        printf("%s\n", con->date[ret].sex);
        printf("电话:>");
        printf("%s\n", con->date[ret].phone);
    }
    else if (ret == -1)
    {
        printf("要查找的联系人不存在\n");
    }
}

//修改通讯录成员信息
void Charge(concat* con)
{
    assert(con);
    printf("请输入要修改联系人的姓名:");
    int ret = find_name(con);
    if (ret != -1)
    {
        printf("修改年龄为:>");
        scanf("%d", &con->date[ret].age);
        printf("修改性别为:>");
        scanf("%s", &con->date[ret].sex);
        printf("修改电话为:>");
        scanf("%s", con->date[ret].phone);
    }
    else if (ret == -1)
    {
        printf("要修改的联系人不存在\n");
    }
}

//显示通讯录
void Display(const concat* con)
{
    assert(con);
    int i = 0;
    printf("%-20s\t%-4s\t%-5s\t%-12s\n", "名字", "年龄", "性别", "电话");
    for (i = 0; i < con->size; i++)
    {
        printf("%-20s\t%-4d\t%-5s\t%-12s\n", con->date[i].name, con->date[i].age, con->date[i].sex, con->date[i].phone);
    }
}

int my_strcmp(void* p1, void* p2)
{
    assert(p1 && p2);
    char* desc = p1;
    char* ser = p2;
    while (*desc != '\0' && *ser != '\0')
    {
        if ((*ser == '\0') || *desc > *ser)
        {
            return 1;//表示前一个大于后一个内容,要交换
        }
        if ((*desc == '\0') || *desc < *ser)
        {
            return -1;//表示前一个小于后一个内容
        }
        desc++;
        ser++;
    }
    return 0;//表示相等
}


//排序通讯录
void C_sort(concat* con)
{
    //排序规则,名字有小到大,在名字相同时,年龄由小到大
    int i = 0;
    for (i = 0; i < con->size; i++)
    {
        //排名字
        int j = 0;
        for (j = i + 1; j < con->size; j++)
        {
            char* n1;
            char* n2;
            n1 = con->date[i].name;
            n2 = con->date[j].name;
            int ret = my_strcmp(n1, n2);
            if (ret == 1)
            {
                //交换
                int n = sizeof(con->date[0]);

                while (n--)
                {
                    char num = *n1;
                    *n1 = *n2;
                    *n2 = num;
                    n1++;
                    n2++;
                }
            }

        }
    }
    printf("排序成功\n");
}

完!点个赞再走呗

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值