乐趣区

关于程序员:精通C语言打造高效便捷的通讯录管理系统

✨✨ 欢送大家来到贝蒂大讲堂✨✨

🎈🎈养成好习惯,先赞后看哦~🎈🎈

所属专栏:C 语言我的项目
贝蒂的主页:Betty‘s blog

引言:

在咱们大抵学习完 C 语言之后,咱们就能够利用目前所学的常识去做一些有意思的我的项目,而明天贝蒂就带大家实现一个通讯录的繁难实现,

本章你可能须要的常识:

  1. 动态内存的应用:动态内存治理
  2. 文件的创立与应用:[文件操作]()

1. 通讯录要求

  1. 通讯录包含每个人的姓名,性别,年龄,电话与地址。
  2. 玩家能够自由选择通讯录的进出。
  3. 玩家能够自在增删查改通讯录中的数据。

2. 多文件治理

为了不便代码的治理和保障通讯录实现逻辑的清晰性,咱们将采纳多文件治理的模式。

  1. 创立头文件 contact.c,蕴含所有头文件(其余源文件只需援用它即可),以及所有通讯录性能的展示。
  2. 创立源文件 contact.c,负责通讯录所有性能的具体代码实现。
  3. 创立源文件 text.c,负责展示通讯录实现的总体逻辑。

3. 通讯录的筹备

3.1 预处理信息

为了不便咱们后续更换通信的信息,咱们能够利用 来定义通讯录的具体信息的大小。

#define MAX 100// 最大人数
#define MAX_NAME 20// 名字最大长度
#define MAX_SEX 5// 性别最大长度
#define MAX_TELE 12// 电话最大长度
#define MAX_ADDR 30// 地址最大长度

3.2 构造体定义

每个人的通讯录都要蕴含姓名,性别,年龄,电话与地址等信息,这时就须要咱们创立一个构造体来方便管理。

typedef struct PeoInfo
{char name[MAX_NAME];// 名字
    int age;// 年龄
    char sex[MAX_SEX];// 性别
    char tele[MAX_TELE];// 电话
    char addr[MAX_ADDR];// 地址
}PeoInfo;

而咱们须要用这个构造体创立一个大小为 100 的数组,并且咱们还 须要晓得以后通讯录的大小能力进行增删查改的操作,这两者非亲非故,为了简化代码和减少代码的可读性,咱们能够将这两者从新定义一个构造体。

typedef struct contact
{PeoInfo data[MAX];// 一百个人的数据
    int sz;// 通讯录的大小
}contact;

4. 繁难菜单

void menu()
{printf("***********************************\n");
    printf("**    1.add          2.delete    **\n");
    printf("**    3.search       4.modify    **\n");
    printf("**    5.display      6.sort      **\n");
    printf("**             0.exit            **\n");
    printf("***********************************\n");
}

画面展现:

5. 通讯录具体性能

5.1 初始化

咱们首先对通讯录进行初始化。

void InitContact(contact* pc)// 初始化
{assert(pc);
    pc->sz = 0;
    memset(pc->data, 0, sizeof(pc->data));
}

5.2 展现联系人

当用户抉择 5 时主动展现通讯录中的用户,并且展现用户过程中 须要进行对齐,便于用户观看。

void DisplayContact(contact* pc)// 打印信息
{assert(pc);
    printf("%-15s\t%-5s\t%-5s\t%-12s\t%-30s\n", "姓名", "年纪",
        "性别", "电话", "地址");// 默认右对齐,批改为左对齐
    // 两头也要留下足够的空间
    for (int i = 0; i < pc->sz; i++)
    {printf("%-15s\t%-5d\t%-5s\t%-12s\t%-30s\n", pc->data[i].name,
            pc->data[i].age,
            pc->data[i].sex,
            pc->data[i].tele,
            pc->data[i].addr);
    }
}

5.3 增加联系人

用户抉择 1 能够自在增加联系人,如果 通讯录已满,则揭示用户通讯录已满,请先清理通讯录。

(1) 查看通讯录是否已满

如果通讯录满了返回 0,未满则返回 1。

int CheckContact(contact*pc)// 查看大小
{assert(pc);
    if (pc->sz == 100)
    {return 0;}
    return 1;
}

(2) 增加

当通讯录未满时,用户能够输出数据增加新用户。

void AddContact(contact* pc)// 减少联系人
{assert(pc);
    int ret = CheckContact(pc);// 查看是否满了
    if (ret == 0)
    {printf("通讯录已满,请先清理通讯录!!\n");
        return;
    }
    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].addr);
    printf("用户增加胜利!\n");
    pc->sz++;
}

5.4 删除联系人

用户能够抉择 2 清理通讯录,删除指定联系人

(1) 寻找下标

在删除指定练习人时咱们需通过其姓名寻找该联系人的下标。找到返回其下标,否则返回 -1、

int FindName(contact* pc, char name[])
{assert(pc&&name);
    for (int pos = 0; pos < pc->sz; pos++)
    {if (strcmp(pc->data[pos].name, name) == 0)
        {return pos;}
    }
    return -1;
}

(2) 删除

通过寻找到的下标,咱们能够利用前面的数据 顺次笼罩 来达到删除的目标。

留神:咱们不能笼罩最初一个数据否则就会产生数组越界,这时咱们只需减去通讯录此时的大小就好了。

void DeleteContact(contact* pc)// 删除联系人
{assert(pc);
         assert(pc->sz >= 0);
    char name[MAX_NAME];
    printf("请输出须要删除人的姓名:>");
    scanf("%s", name);
    int pos = FindName(pc, name);
    if (pos == -1)
    {printf("通讯录中并没有这个人!!\n");
        return;
    }
    for (int i = pos; i < pc->sz - 1; i++)
    {pc->data[i] = pc->data[i + 1];// 笼罩
    }
        printf("删除胜利 \n");pc->sz--;
}

删除前:

删除后 :

5.5 查找联系人

通过抉择 3 查找指定联系人,没有找到则揭示用户没有该用户,查找到就打印其信息。

void SearchContact(contact* pc)// 查找联系人
{assert(pc);
    char name[MAX_NAME];
    printf("请输出须要查找人的姓名:>");
    scanf("%s", name);
    int pos = FindName(pc, name);
    if (pos == -1)
    {printf("通讯录中并没有这个人!!\n");
        return;
    }
    printf("%-15s\t%-5s\t%-5s\t%-12s\t%-30s\n", "姓名", "年纪",
        "性别", "电话", "地址");
    printf("%-15s\t%-5d\t%-5s\t%-12s\t%-30s", pc->data[pos].name,
        pc->data[pos].age,
        pc->data[pos].sex,
        pc->data[pos].tele,
        pc->data[pos].addr);
}

5.6 批改联系人

咱们能够通过抉择 4 批改指定联系人的信息。

void ModifyContact(contact* pc)// 批改联系人
{assert(pc);
    char name[MAX_NAME];
    printf("请输出须要批改人的姓名:>");
    scanf("%s", name);
    int pos = FindName(pc, name);
    if (pos == -1)
    {printf("通讯录中并没有这个人!!\n");
        return;
    }
    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].addr);
    printf("批改胜利 \n");
}

批改前:

批改后:

5.7 排序联系人

咱们能够抉择 6 对通讯录进行排序,能够依照姓名,年纪,性别,电话,地址排序。

int cmp1(const void* p1, const void* p2)
{return strcmp(((PeoInfo*)p1)->name, ((PeoInfo*)p2)->name);
}
int cmp2(const void* p1, const void* p2)
{return ((PeoInfo*)p1)->age- ((PeoInfo*)p2)->age;
}
int cmp3(const void* p1, const void* p2)
{return strcmp(((PeoInfo*)p1)->sex, ((PeoInfo*)p2)->sex);
}
int cmp4(const void* p1, const void* p2)
{return strcmp(((PeoInfo*)p1)->tele, ((PeoInfo*)p2)->tele);
}
int cmp5(const void* p1, const void* p2)
{return strcmp(((PeoInfo*)p1)->addr, ((PeoInfo*)p2)->addr);
}
void SortByName(contact* pc)
{qsort(pc, pc->sz, sizeof(PeoInfo), cmp1);
}
void SortByAge(contact* pc)
{qsort(pc, pc->sz, sizeof(PeoInfo), cmp2);
}
void SortBySex(contact* pc)
{qsort(pc, pc->sz, sizeof(PeoInfo), cmp3);
}
void SortByTele(contact* pc)
{qsort(pc, pc->sz, sizeof(PeoInfo), cmp4);
}
void SortByAddr(contact* pc)
{qsort(pc, pc->sz, sizeof(PeoInfo), cmp4);
}
void SortContact(contact* pc)// 排序联系人
{assert(pc);
    printf("请抉择如何排序:>");
    char input[20];
    scanf("%s", input);
    if (strcmp(input, "姓名")==0)
    {SortByName(pc);// 按姓名排序
    }
    else if (strcmp(input, "年龄") == 0)
    {SortByAge(pc);// 按年龄排序
    }
    else if (strcmp(input, "性别") == 0)
    {SortBySex(pc);// 按性别排序
    }
    else if (strcmp(input, "电话") == 0)
    {SortByTele(pc);// 按电话排序
    }
    else if (strcmp(input, "地址") == 0)
    {SortByAddr(pc);// 按地址排序
    }
    else
    {printf("输出非法,请从新输出 \n");
    }
}

6. 改良通讯录

6.1 动态内存开拓

上述通讯录有一个致命的毛病——通讯录大小固定,为了解决这个问题我能够应用咱们后面学过的 动态内存开拓

(1) 新增变量

为了不便咱们晓得此时的容量大小,咱们将在构造体中退出新的变量。

typedef struct contact
{
    PeoInfo *data;// 动静开拓的数据
    int sz;// 通讯录的大小
    int capacity;// 通讯录的容量
}contact;

(2) 初始化

void InitContact(contact* pc)// 初始化
{assert(pc);
    pc->sz = 0;
    pc->data = (PeoInfo*)calloc(2, sizeof(PeoInfo));
    if (pc->data == NULL)
    {perror("InitContact:");
        return;
    }
    pc->capacity = 2;
    /*memset(pc->data, 0, sizeof(pc->data));*/
}

(3) 增容

当通讯录用户数量满时减少内存空间。

void CheckCapacity(contact* pc)// 增容
{if (pc->sz == pc->capacity)
    {PeoInfo* tmp = (PeoInfo*)realloc(pc->data, (pc->capacity + 2) * sizeof(PeoInfo));
        if (tmp != NULL)
        {pc->data = tmp;}
        else
        {perror("CheckCapacity:");
            return;
        }
        pc->capacity += 2;
        printf("增容胜利 \n");
    }
}

6.2 文件保留

咱们发现每当咱们关闭程序时咱们写入的数据会被清空,咱们要想保留上次写入的数据就应该应用 文件操作

(1) 加载数据

每次初始化时就要将上次文件的数据导入进来。

void LoadContact(contact* pc)// 加载上次数据
{PeoInfo tmp = { 0};
    FILE* pf = fopen("contact.txt", "rb");
    if (pf == NULL)
    {return;}
    // 读取文件,寄存到通讯录
    while (fread(&tmp, sizeof(PeoInfo), 1, pf))
    // 返回值为 0 就是遇见文件开端
    {CheckCapacity(pc);
        pc->data[pc->sz] = tmp;
        pc->sz++;
    }

    fclose(pf);
    pf = NULL;
}

(2) 保留数据

当程序失常完结时保留数据。

void SaveContact(contact* pc)// 保留数据
{FILE* pfWrite = fopen("contact.txt", "wb");
    if (pfWrite == NULL)
    {perror("fopen:");
        return;
    }
    // 写通讯录中数据到文件中
    int i = 0;
    for (i = 0; i < pc->sz; i++)
    {fwrite(&(pc->data[i]), sizeof(PeoInfo), 1, pfWrite);
    }
    fclose(pfWrite);
    pfWrite = NULL;
    printf("保留胜利 \n");

}

7. 通讯录逻辑的搭建

  1. 用户通过数字抉择性能。
  2. 每次抉择完之后返回菜单。
  3. 抉择退出之后保留数据,程序完结。
enum Option
    // 利用枚举减少代码的可读性
{
    EXIT,//0
    ADD,
    DEL,
    SEARCH,
    MODIFY,
    DISPLAY,
    SORT
};
void test()
{
    int input = 0;
    contact con;
    InitContact(&con);
    do
    {menu();
        printf("请输出你的抉择:>");
        scanf("%d", &input);
        switch (input)
        {
        case ADD:
            AddContact(&con);
            break;
        case DEL:
            DeleteContact(&con);
            break;
        case SEARCH:
            SearchContact(&con);
            break;
        case MODIFY:
            ModifyContact(&con);
            break;
        case DISPLAY:
            DisplayContact(&con);
            break;
        case SORT:
            SortContact(&con);
            break;
        case EXIT:
            SaveContact(&con);// 退出保留数据
            DestroyContact(&con);
            break;
        default:
            printf("非法输出,请从新输出 \n");
            break;
        }
    } while (input);
}
int main()
{test();
    return 0;
}

8. 残缺代码

8.1 contact.h

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<assert.h>
#define MAX_NAME 20// 名字最大长度
#define MAX_SEX 5// 性别最大长度
#define MAX_TELE 12// 电话最大长度
#define MAX_ADDR 30// 地址最大长度
typedef struct PeoInfo
{char name[MAX_NAME];// 名字
    int age;// 年龄
    char sex[MAX_SEX];// 性别
    char tele[MAX_TELE];// 电话
    char addr[MAX_ADDR];// 地址
}PeoInfo;
typedef struct contact
{
    PeoInfo *data;// 动静开拓的数据
    int sz;// 通讯录的大小
    int capacity;// 通讯录的容量
}contact;
void InitContact(contact* pc);// 初始化
void DisplayContact(contact* pc);// 打印信息
void AddContact(contact* pc);// 减少联系人
void DeleteContact(contact* pc);// 删除联系人
void SearchContact(contact* pc);// 查找联系人
void ModifyContact(contact* pc);// 批改联系人
void SortContact(contact* pc);// 排序联系人
void SaveContact(contact* pc);// 保留数据
void DestroyContact(contact* pc);// 销毁内存

8.2 contact.c

#include"contact.h"
void CheckCapacity(contact* pc)// 增容
{if (pc->sz == pc->capacity)
    {PeoInfo* tmp = (PeoInfo*)realloc(pc->data, (pc->capacity + 2) * sizeof(PeoInfo));
        if (tmp != NULL)
        {pc->data = tmp;}
        else
        {perror("CheckCapacity:");
            return;
        }
        pc->capacity += 2;
        printf("增容胜利 \n");
    }
}
void LoadContact(contact* pc)// 加载上次数据
{PeoInfo tmp = { 0};
    FILE* pf = fopen("contact.txt", "rb");
    if (pf == NULL)
    {return;}
    // 读取文件,寄存到通讯录
    while (fread(&tmp, sizeof(PeoInfo), 1, pf))
    // 返回值为 0 就是遇见文件开端
    {CheckCapacity(pc);
        pc->data[pc->sz] = tmp;
        pc->sz++;
    }

    fclose(pf);
    pf = NULL;
}

void InitContact(contact* pc)// 初始化
{assert(pc);
    pc->sz = 0;
    pc->data = (PeoInfo*)calloc(2, sizeof(PeoInfo));
    if (pc->data == NULL)
    {perror("InitContact:");
        return;
    }
    pc->capacity = 2;
    LoadContact(pc);
    /*memset(pc->data, 0, sizeof(pc->data));*/
}
void DisplayContact(contact* pc)// 打印信息
{assert(pc);
    printf("%-15s\t%-5s\t%-5s\t%-12s\t%-30s\n", "姓名", "年纪",
        "性别", "电话", "地址");// 默认右对齐,批改为左对齐
    // 两头也要留下足够的空间
    for (int i = 0; i < pc->sz; i++)
    {printf("%-15s\t%-5d\t%-5s\t%-12s\t%-30s\n", pc->data[i].name,
            pc->data[i].age,
            pc->data[i].sex,
            pc->data[i].tele,
            pc->data[i].addr);
    }
}

void AddContact(contact* pc)// 减少联系人
{assert(pc);
    CheckCapacity(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].addr);
    printf("用户增加胜利!\n");
    pc->sz++;
}
int FindName(contact* pc, char name[])
{assert(pc&&name);
    for (int pos = 0; pos < pc->sz; pos++)
    {if (strcmp(pc->data[pos].name, name) == 0)
        {return pos;}
    }
    return -1;
}
void DeleteContact(contact* pc)// 删除联系人
{assert(pc);
    assert(pc->sz >= 0);
    char name[MAX_NAME];
    printf("请输出须要删除人的姓名:>");
    scanf("%s", name);
    int pos = FindName(pc, name);
    if (pos == -1)
    {printf("通讯录中并没有这个人!!\n");
        return;
    }
    for (int i = pos; i < pc->sz - 1; i++)
    {pc->data[i] = pc->data[i + 1];// 笼罩
    }
    printf("删除胜利 \n");
    pc->sz--;
}
void SearchContact(contact* pc)// 查找联系人
{assert(pc);
    char name[MAX_NAME];
    printf("请输出须要查找人的姓名:>");
    scanf("%s", name);
    int pos = FindName(pc, name);
    if (pos == -1)
    {printf("通讯录中并没有这个人!!\n");
        return;
    }
    printf("%-15s\t%-5s\t%-5s\t%-12s\t%-30s\n", "姓名", "年纪",
        "性别", "电话", "地址");
    printf("%-15s\t%-5d\t%-5s\t%-12s\t%-30s", pc->data[pos].name,
        pc->data[pos].age,
        pc->data[pos].sex,
        pc->data[pos].tele,
        pc->data[pos].addr);
}
void ModifyContact(contact* pc)// 批改联系人
{assert(pc);
    char name[MAX_NAME];
    printf("请输出须要批改人的姓名:>");
    scanf("%s", name);
    int pos = FindName(pc, name);
    if (pos == -1)
    {printf("通讯录中并没有这个人!!\n");
        return;
    }
    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].addr);
    printf("批改胜利 \n");
}
int cmp1(const void* p1, const void* p2)
{return strcmp(((PeoInfo*)p1)->name, ((PeoInfo*)p2)->name);
}
int cmp2(const void* p1, const void* p2)
{return ((PeoInfo*)p1)->age- ((PeoInfo*)p2)->age;
}
int cmp3(const void* p1, const void* p2)
{return strcmp(((PeoInfo*)p1)->sex, ((PeoInfo*)p2)->sex);
}
int cmp4(const void* p1, const void* p2)
{return strcmp(((PeoInfo*)p1)->tele, ((PeoInfo*)p2)->tele);
}
int cmp5(const void* p1, const void* p2)
{return strcmp(((PeoInfo*)p1)->addr, ((PeoInfo*)p2)->addr);
}
void SortByName(contact* pc)
{qsort(pc, pc->sz, sizeof(PeoInfo), cmp1);
}
void SortByAge(contact* pc)
{qsort(pc, pc->sz, sizeof(PeoInfo), cmp2);
}
void SortBySex(contact* pc)
{qsort(pc, pc->sz, sizeof(PeoInfo), cmp3);
}
void SortByTele(contact* pc)
{qsort(pc, pc->sz, sizeof(PeoInfo), cmp4);
}
void SortByAddr(contact* pc)
{qsort(pc, pc->sz, sizeof(PeoInfo), cmp4);
}
void SortContact(contact* pc)// 排序联系人
{assert(pc);
    printf("请抉择如何排序:>");
    char input[20] = {0};
    scanf("%s", input);
    if (strcmp(input, "姓名")==0)
    {SortByName(pc);// 按姓名排序
    }
    else if (strcmp(input, "年龄") == 0)
    {SortByAge(pc);// 按年龄排序
    }
    else if (strcmp(input, "性别") == 0)
    {SortBySex(pc);// 按性别排序
    }
    else if (strcmp(input, "电话") == 0)
    {SortByTele(pc);// 按电话排序
    }
    else if (strcmp(input, "地址") == 0)
    {SortByAddr(pc);// 按地址排序
    }
    else
    {printf("输出非法,请从新输出 \n");
    }
}
void DestroyContact(contact* pc)
{free(pc->data);
    pc->data= NULL;
    pc->capacity = 0;
    pc->sz = 0;
    printf("销毁胜利 \n");
}
void SaveContact(contact* pc)// 保留数据
{FILE* pfWrite = fopen("contact.txt", "wb");
    if (pfWrite == NULL)
    {perror("fopen:");
        return;
    }
    // 写通讯录中数据到文件中
    int i = 0;
    for (i = 0; i < pc->sz; i++)
    {fwrite(&(pc->data[i]), sizeof(PeoInfo), 1, pfWrite);
    }
    fclose(pfWrite);
    pfWrite = NULL;
    printf("保留胜利 \n");

}

8.3 text.c

#include"contact.h"

enum Option
    // 利用枚举减少代码的可读性
{
    EXIT,//0
    ADD,
    DEL,
    SEARCH,
    MODIFY,
    DISPLAY,
    SORT
};

void menu()
{printf("***********************************\n");
    printf("**    1.add          2.delete    **\n");
    printf("**    3.search       4.modify    **\n");
    printf("**    5.display      6.sort      **\n");
    printf("**             0.exit            **\n");
    printf("***********************************\n");
}
void test()
{
    int input = 0;
    contact con;
    InitContact(&con);
    do
    {menu();
        printf("请输出你的抉择:>");
        scanf("%d", &input);
        switch (input)
        {
        case ADD:
            AddContact(&con);
            break;
        case DEL:
            DeleteContact(&con);
            break;
        case SEARCH:
            SearchContact(&con);
            break;
        case MODIFY:
            ModifyContact(&con);
            break;
        case DISPLAY:
            DisplayContact(&con);
            break;
        case SORT:
            SortContact(&con);
            break;
        case EXIT:
            SaveContact(&con);// 退出保留数据
            DestroyContact(&con);
            break;
        default:
            printf("非法输出,请从新输出 \n");
            break;
        }
    } while (input);
}

int main()
{test();
    return 0;
}
退出移动版