通讯录管理系统的设计与实现

以最简单的方式实现几个C语言课程设计的常见题目,适合大一或者刚学习C语言的同学学习参考。使用Code::Blocks编译器创建的纯C项目,将其中的源码粘贴进其他编译器或C++项目也可直接运行。因为部分同学没有学习过数据结构,所以尽量使用传统的数组进行存储,规避没有学习过的知识点,但鼓励大家自己改进。为了使得程序更加简单方便阅读,基本上没有进行对用户输入的容错,可以自己添加。

Code::Blocks安装和使用 https://blog.csdn.net/qq_42283621/article/details/124055391?spm=1001.2014.3001.5501

项目源码会在文章末尾给出

功能划分

为了简单,我们用一个数组data来存储 通讯录记录,保存和加载的文件为“addressbook.txt”,函数均使用无参数的函数,且不考虑姓名重复问题

共6个功能

  1. 退出系统

  2. 录入 enter

    通讯录 为空 则提示"通讯录空";提醒用户输入“姓名”和“号码”,存入通讯录;

  3. 删除 del

    提醒用户输入“姓名”,然后在通讯录中删除该条记录,输出“删除成功”或“<姓名>不存在,删除失败”

  4. 显示 list

    输出通讯录中的所有记录

  5. 查询 search

    提醒用户输入“姓名”,输出该姓名的通讯录信息 或者 “<姓名>不存在,查询失败”

  6. 保存 save

    将 通讯录 中的所有记录写入到 文件addressbook.txt 中,输出“保存成功”

  7. 加载 load

    将 文件addressbook.txt 中的内容加载到通讯录中,输出“加载成功”

总体框架

首先,我们需要定义一个结构体来存储一条记录,存储的内容包括 姓名 和 电话号,姓名长度是不定的,电话号是11位数(但字符数组会自动在最后添加一个’\0’作为结束标志,所以至少需要12位,当然你也可以定义一个更大的)。测试程序时使用的 姓名 用英语不要用中文,因为中文可能需要其他处理才能正常显示

然后,定义一个数组来存储所有的通讯录信息,我们可以定义一个比较大的这样可以存储更多的信息。因为数组是固定大小的,所以我们并不知道到底里边存入了多少条信息,可以定义一个int cnt表示通讯录中的信息条数,初始时cnt为0表示没有信息,当cnt与你定义的数组大小一样大时,说明已经存满了。

struct Item
{
    
    
    char name[20];
    char phone[20];
};

struct Item data[100];
int cnt = 0;

然后,我们要不断获取用户下一步的操作,也就是我们应该将 获取用户下一步操作 放入到循环中。当用户下一步操作是“退出系统”时,退出循环,是其他时分别调用不同的函数进行相应的处理。每次循环的开始,提示用户输入什么数字是什么意思。

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

struct Item
{
    
    
    char name[20];
    char phone[20];
};

struct Item data[100];
int cnt = 0;

void enter()
{
    
    

}

void del()
{
    
    

}

void list()
{
    
    

}

void search()
{
    
    

}

void save()
{
    
    

}

void load()
{
    
    

}

int main()
{
    
    
    int cmd;
    while(1)
    {
    
    
        printf("\n\n\n请输入下一步操作:\n");
        printf("\t\t0 退出系统\n");
        printf("\t\t1 录入记录\n");
        printf("\t\t2 删除记录\n");
        printf("\t\t3 显示记录\n");
        printf("\t\t4 查询记录\n");
        printf("\t\t5 保存记录\n");
        printf("\t\t6 加载记录\n");
        scanf("%d", &cmd);
        if (cmd == 0)
        {
    
    
            printf("退出系统成功");
            break;
        }
        switch(cmd)
        {
    
    
            case 1: enter(); break;
            case 2: del(); break;
            case 3: list(); break;
            case 4: search(); break;
            case 5: save(); break;
            case 6: load(); break;
        }
    }
    return 0;
}

函数实现

  1. 先写enter和list,然后可以进行简单的测试
void enter()
{
    
    
    if (cnt == 0)
        printf("通讯录空\n");
    printf("输入:姓名 电话\n");
    scanf("%s %s", data[cnt].name, data[cnt].phone);
    ++cnt;//通讯录数目+1
}

void list()
{
    
    
    printf("\t\t姓名\t\t电话\n");
    for (int i = 0; i < cnt; ++i)
    {
    
    
        printf("\t\t%s\t\t%s\n", data[i].name, data[i].phone);
    }
}

image-20220419154520153

  1. del

    删除首先需要比较输入的“姓名”和通讯录中存储的“姓名”是否是一样的,使用int strcmp(const char *s1, const char *s2),s1和s2相同则返回0,其在头文件#include <string.h>中。还要用到char *strcpy(char* dest, const char *src)将一个字符数组的内容复制到另一个数组,同样在string.h中。

    然后是删除的过程,我们可以采用将要删除的记录后面的信息向前覆盖的方式,下面是图解。我们有5条记录,cnt为5,分别位于位置0 1 2 3 4。删除Tom,即位于3的Bob覆盖位于2的Tom,位于4的Jack覆盖位于3的Bob,然后将cnt-1成为4。在最后的图上看来,Tom没有了但Jack的信息有两条,但是我们的cnt为4,即只有第0 1 2 3是有效的,位于4的Jack的信息在主观上认为他被废弃了即可。

    image-20220419103513259

void del()
{
    
    
    char tName[20];
    printf("输入:姓名\n");
    scanf("%s", tName);

    for (int i = 0; i < cnt; ++i)
    {
    
    
        if (strcmp(tName, data[i].name) == 0)
        {
    
    
            for (int j = i; j < cnt - 1; ++j)
            {
    
    
                strcpy(data[j].name, data[j + 1].name);
                strcpy(data[j].phone, data[j + 1].phone);
            }
            printf ("删除成功\n");
            --cnt;
            break;
        }
        else if (i == cnt - 1)
            printf ("%s不存在,删除失败\n", tName);
    }
}
  1. search就是一个简单的遍历,用到的函数在del中都有
void search()
{
    
    
    char tName[20];
    printf("输入:姓名\n");
    scanf("%s", tName);
    
    for (int i = 0; i < cnt; ++i)
    {
    
    
        if (strcmp(tName, data[i].name) == 0)
        {
    
    
            printf("\t\t姓名\t\t电话\n");
            printf("\t\t%s\t\t%s\n", data[i].name, data[i].phone);
            break;
        }
        else if (i == cnt - 1)
            printf ("%s不存在,查询失败\n", tName);
    }
}

到此为止,用到的无关文件的操作已经完成。

  1. load和save

    首先,我们先准备一个测试文件txt,使用txt文件是因为这是windows中最简单的文件,一般是我们接触程序操作文件的第一种文件类型。桌面上 右键->新建->文本文档->重命名为addressbook,然后在其中写入几条测试文件。

    image-20220419105612080

    image-20220419105603598

    然后,将这个文件复制到项目的根目录里,我用的Code::Blocks,根目录在main.cpp所在的路径下,这样我们可以直接使用文件名,不用在filename中写出文件的完整路径。

    image-20220419145437164

    接下来,我们熟悉一下文件的相关操作:打开文件 读文件 写文件 关闭文件

    1)打开关闭文件

    ​ FILE *fopen(const char *filename, const char *mode)

    ​ 参数1为文件名,参数2为模式,返回值不是NULL则证明打开成功了。我们用到的文件名为"addressbook.txt",模式在保存的是写"w",在加载的时候读"r"

    ​ save中: FILE * fp; fp = fopen(“addressbook.txt”, “w”)

    ​ load中:FILE * fp; fp = fopen(“addressbook.txt”, “r”)

    ​ int fclose(FILE *stream)关闭文件

    2)读文件和写文件

    ​ C语言读文件和写文件的方法有很多,fread fwrite fgets fputs fscanf fprintf,这里我们使用fscanf和fprintf搭配,它和scanf和printf基本相同的用法,但在开始加一个文件的指针。

    ​ int fscanf(FILE *stream, const char *format, …)

    ​ int fprintf(FILE *stream, const char *format, …)

    返回的int表示读写成功的个数,到这里你可能不太明白,你可以结合例子一起看

    我们定义的txt中数据的保存格式是一条记录占一行,然后 姓名+空格+电话,所以读的时候fscanf(fp, “%s %s”, data[cnt].name, data[cnt].phone),即将txt一行中的两个字符串分别读到.name和.phone中,如果这个函数返回的是2,说明我们读成功了,cnt加一。如果不是2,说明我们读失败了,也就是说后面已经没有数据了。

    写文件fprintf(fp, “%s %s\n”, data[i].name, data[i].phone);,即将.name和.phone中的数据按照格式"%s %s\n"写入到文件中。

void save()
{
    
    
    FILE *fp;
    if ((fp = fopen("addressbook.txt", "w")) != NULL)
    {
    
    
        for (int i = 0; i < cnt; ++i)
            fprintf(fp, "%s %s\n", data[i].name, data[i].phone);
        fclose(fp);
        printf("保存成功\n");
    }
    else
    {
    
    
        printf("保存失败\n");
    }
}

void load()
{
    
    
    FILE *fp;
    if ((fp = fopen("addressbook.txt", "r")) != NULL)
    {
    
    
        while (fscanf(fp, "%s %s", data[cnt].name, data[cnt].phone) == 2)
        {
    
    
            ++cnt;
        }
        fclose(fp);
        printf("加载成功\n");
    }
    else
    {
    
    
        printf("加载失败\n");
    }
}

总体效果

最后,我们来测试一下整个程序

  1. 加载 显示

image-20220419154813641

  1. 录入 显示

image-20220419154835723

  1. 删除 显示

image-20220419154854923

  1. 查询

    image-20220419155043352

  2. 保存,并查看文件中存的数据是否正确

    image-20220419155111776

image-20220419154950541

附录

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

struct Item
{
    
    
    char name[20];
    char phone[20];
};

struct Item data[100];
int cnt = 0;

void enter()
{
    
    
    if (cnt == 0)
        printf("通讯录空\n");
    printf("输入:姓名 电话\n");
    scanf("%s %s", data[cnt].name, data[cnt].phone);
    ++cnt;//通讯录数目+1
}

void del()
{
    
    
    char tName[20];
    printf("输入:姓名\n");
    scanf("%s", tName);

    for (int i = 0; i < cnt; ++i)
    {
    
    
        if (strcmp(tName, data[i].name) == 0)
        {
    
    
            for (int j = i; j < cnt - 1; ++j)
            {
    
    
                strcpy(data[j].name, data[j + 1].name);
                strcpy(data[j].phone, data[j + 1].phone);
            }
            printf ("删除成功\n");
            --cnt;
            break;
        }
        else if (i == cnt - 1)
            printf ("%s不存在,删除失败\n", tName);
    }
}

void list()
{
    
    
    printf("\t\t姓名\t\t电话\n");
    for (int i = 0; i < cnt; ++i)
    {
    
    
        printf("\t\t%s\t\t%s\n", data[i].name, data[i].phone);
    }
}

void search()
{
    
    
    char tName[20];
    printf("输入:姓名\n");
    scanf("%s", tName);

    for (int i = 0; i < cnt; ++i)
    {
    
    
        if (strcmp(tName, data[i].name) == 0)
        {
    
    
            printf("\t\t姓名\t\t电话\n");
            printf("\t\t%s\t\t%s\n", data[i].name, data[i].phone);
            break;
        }
        else if (i == cnt - 1)
            printf ("%s不存在,查询失败\n", tName);
    }
}

void save()
{
    
    
    FILE *fp;
    if ((fp = fopen("addressbook.txt", "w")) != NULL)
    {
    
    
        for (int i = 0; i < cnt; ++i)
            fprintf(fp, "%s %s\n", data[i].name, data[i].phone);
        fclose(fp);
        printf("保存成功\n");
    }
    else
    {
    
    
        printf("保存失败\n");
    }
}

void load()
{
    
    
    FILE *fp;
    if ((fp = fopen("addressbook.txt", "r")) != NULL)
    {
    
    
        while (fscanf(fp, "%s %s", data[cnt].name, data[cnt].phone) == 2)
        {
    
    
            ++cnt;
        }
        fclose(fp);
        printf("加载成功\n");
    }
    else
    {
    
    
        printf("加载失败\n");
    }
}

int main()
{
    
    
    int cmd;
    while(1)
    {
    
    
        printf("\n\n\n请输入下一步操作:\n");
        printf("\t\t0 退出系统\n");
        printf("\t\t1 录入记录\n");
        printf("\t\t2 删除记录\n");
        printf("\t\t3 显示记录\n");
        printf("\t\t4 查询记录\n");
        printf("\t\t5 保存记录\n");
        printf("\t\t6 加载记录\n");
        scanf("%d", &cmd);
        if (cmd == 0)
        {
    
    
            printf("退出系统成功\n");
            break;
        }
        switch(cmd)
        {
    
    
            case 1: enter(); break;
            case 2: del(); break;
            case 3: list(); break;
            case 4: search(); break;
            case 5: save(); break;
            case 6: load(); break;
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_42283621/article/details/124275929