清水伊莲 發表於 2025-11-28 22:33:00

用C语言和文本文件实现一个简单的,可保存的通讯录

<p>我们先思考一个通讯录都有那些信息,很明显通讯录记录的是人<br>
人有哪些信息呢<br>
这里我就写5个吧,分别是姓名,年龄,电话,性别,地址<br>
然后我们把他们写成一个结构体,最好定义在头文件里,这样在使用的时候更方便<br>
我们还可以把要使用的一些常用的或者要修改的常量定义成枚举,后续有什么要添加的,直接就能在枚举中添加</p>
<pre><code>//枚举的一些关键常量
enum NUM
{
        NAME = 20, //名字
        TELE = 12,//电话
        SEX = 10,//性别
        ADDR = 100,//地址
        ADD = 1,DEL = 2,SEARCH = 3,MODIFY = 4,SHOW = 5,SORT = 6,EXIT = 0,CLEAR =7,//菜单
        //ARRDATA = 100
        SIZE = 3,//通讯录初始容量大小
        SIZEADD = 2//每次扩容的增加量
};
//人的信息
typedef struct peo
{
        char name;   //姓名
        int age;            //年龄
        char tele;//电话
        char sex;    //性别
        char addr;   //地址
}peo;
</code></pre>
<p>这里的枚举内容我直接把参数都写里了,后续在思考的过程就不再改动了<br>
接下来思考,光有一个人的信息是不够的,无法完整表达一个通讯录,当然,由于我们只是简单实现,我们就只再记录通讯录的人员数量和通讯录的容量<br>
把他们简单封装成一个结构体</p>
<pre><code>typedef struct contact
{
        peo* data;          //人的相关数据
        size_t count;   //成员数量
        size_t capacity;   //通讯录容量
}contact;
</code></pre>
<p>这里我都是进行了重命名了的,方便后续的使用<br>
这些都是头文件里面要用的,还有一些库函数,我就在这里提前定义了</p>
<pre><code>#include&lt;stdio.h&gt;
#include&lt;string.h&gt;
#include&lt;assert.h&gt;
#include&lt;stdlib.h&gt;
#include&lt;errno.h&gt;
</code></pre>
<p>然后我们写主函数<br>
我们考虑通讯录运行一定要有菜单<br>
然后我们用switch语句写分支结构<br>
我们需要用户主动输入选择,所以我们还需要一个变量接收,这个变量还可以用于switch语句的判断<br>
菜单我们可以单独封装一个函数<br>
可以实现增加联系人,删除联系人,查找联系人,修改联系人,显示联系人,按名字排序联系人,清空联系人,和退出保存,将数据保存到文件中,把这些功能都封装成函数<br>
还得创建一个结构体变量用于存放通讯录的数据<br>
还需要一个函数对结构体进行初始化,并把保存在文件中的信息放入到结构体中<br>
于是主函数如下</p>
<pre><code>#include"contact.h"
static void menu()   //菜单函数
{
        printf("****************************************\n");
        printf("****************************************\n");
        printf("****************************************\n");
        printf("***       1.add      2.del      ******\n");
        printf("***       3.search   4.modify   ******\n");
        printf("***       5.show   6.sort       ******\n");
        printf("***       0.exit   7.clear      ******\n");
        printf("****************************************\n");
        printf("****************************************\n");
        printf("****************************************\n");
}
int main()
{
        int input = 0;      //存放菜单选择输入的值
        contact con;   //创建存放通讯录数据的结构体
        init_contact(&amp;con);//初始化结构体
        do
        {
                menu();
                printf("请输入要进行的功能\n");
                scanf("%d", &amp;input);      //菜单的选择输入
                switch (input)            //菜单选择的判断逻辑
                {
                case ADD:
                        add_contact(&amp;con);       //添加函数
                        break;
                case DEL:
                        del_contact(&amp;con);       //删除函数
                        break;
                case SEARCH:
                        search_contact(&amp;con);    //查找函数
                        break;
                case MODIFY:
                        modify_contact(&amp;con);    //修改函数
                        break;
                case SHOW:
                        show_contact(&amp;con);      //显示函数
                        break;
                case SORT:
                        sort_contact(&amp;con);       //排序函数
                        break;
                case CLEAR:
                        clear_contact(&amp;con);   //清空函数
                        break;
                case EXIT:
                        save_contact(&amp;con);       //保存函数
                        destroycontact(&amp;con);      //推出后内存的销毁
                        printf("程序退出\n");
                        break;
                default:
                        printf("选择错误,请重新选择\n");//其他逻辑判断
                        break;
                }
        } while (input)//do while的循环判断
        return 0;
}
</code></pre>
<p>主函数别忘了引用头文件<br>
接下来我们封装函数<br>
把函数单独放在一个源文件</p>
<pre><code>#include"contact.h"
//初始化函数
//对于初始化我们不仅要初始化结构体内容,还要把之前保存在文件中的信息放入到结构体中,当然还涉及到一个动态扩容的问题,我们的结构体初始设计的是有容量的,如果里面放的信息多了,还要涉及到扩容的问题,
这些都要设计函数
static void expandCapacity(contact* con)//扩容
{
        assert(con);//断言防止空指针
        if (con-&gt;capacity == con-&gt;count)   //容量判断
        {
                void* p = 0;
                p = realloc(con-&gt;data, (con-&gt;capacity + SIZEADD) * sizeof(peo));//追加信息容量
                if (NULL == p) //报错判断
                {
                        printf("expandCapacity::%s\n", strerror(errno));
                        return;
                }
                con-&gt;capacity += SIZEADD;//修改容量标记
                con-&gt;data = (peo*)p;       //修改指针位置(其实没变)
                printf("增容成功\n");
        }
}
static int read_conact(contact* con)   //读取文件中保存的通讯录信息
{
        static void expandCapacity(contact * con);   //函数声明
        FILE* p = fopen("contact.txt", "r");      //打开文件
        if (p == NULL)                        //报错判断
        {
                return 1;
        }
        peo a = { 0 };
        while (fread(&amp;a, sizeof(peo), 1, p) == 1)//循环提取信息,这里取决于保存函数的写法
        {
                expandCapacity(con);//扩容
                con-&gt;data = a;//赋值
                (con-&gt;count)++;//成员计数加一
        }
        fclose(p);
        p = NULL;//关闭文件,指针空置
        return 0;
}
void init_contact(contact* con)    //初始化函数
{
        static int read_conact(contact * con);//函数调用声明
        assert(con);////断言防止空指针
        con-&gt;count = 0;//初始化人数
        void* p = 0;//内存指针
        p = calloc(SIZE, sizeof(peo));//创建内存
        if (NULL == con-&gt;data)//报错判断
        {
                printf("init_contact:calloc:%s\n", strerror(errno));
                return;
        }
        con-&gt;data = (peo*)p;//初始化人信息指针
        con-&gt;capacity = SIZE;//初始化容量
        if (read_conact(con) == 1)//读取之前的数据
        {
                perror("读取旧数据失败");//问题判断
                return;
        }
}

//初始化我们已经完成了
//接下来我们写添加函数
void add_contact(contact* con)
{
        static void expandCapacity(contact * con);//函数声明
        assert(con);//断言防止空指针
        expandCapacity(con);//扩容判断

        printf("请输入姓名\n");
        scanf("%s", (con-&gt;data+con-&gt;count)-&gt;name);
        printf("请输入年龄\n");
        scanf("%d", &amp;((con-&gt;data + con-&gt;count)-&gt;age));
        printf("请输入电话\n");
        scanf("%s", con-&gt;data.tele);
        printf("请输入性别\n");
        scanf("%s", con-&gt;data.sex);
        printf("请输入地址\n");
        scanf("%s", con-&gt;data.addr);//这些都是信息是输入
        con-&gt;count++;//计数加一
        printf("增加成功\n");
}


//接下来我们写显示函数
void show_contact(contact* con)
{
        assert(con);//断言防止空指针
        int i = 0;
        printf("%-20s\t%-5s\t%5s\t%12s\t%30s\n", "姓名", "年龄", "电话", "性别", "地址");
        //显示时方便观察,打印一个标签
        for (i = 0; i &lt; con-&gt;count; i++)//循环打印人员信息
        {
                printf("%-20s\t%-5d\t%-5s\t%-12s\t%-30s\n",
                        con-&gt;data.name,
                        con-&gt;data.age,
                        con-&gt;data.tele,
                        con-&gt;data.sex,
                        con-&gt;data.addr);
        }
}

//查找函数
//查找函数不仅要能查找,还要能显示,我们分成两个函数,因为删除也需要查找
//查找部分
static int find_by_name(const contact* con,const char* arr)//依据名字查找
{
        assert(con &amp;&amp; arr);//断言防止空指针
        int i = 0;
        for (i = 0; i &lt; con-&gt;count; i++)//循环遍历查找
        {
                if (0 == strcmp(con-&gt;data.name, arr))
                {
                        return i;//找到返回下标
                }
        }
        return -1;//找不到返回-1
}

//查找
void search_contact(contact* con)
{
        static int find_by_name(const contact * con, const char* arr);//函数声明
        assert(con);//断言防止空指针
        if (con-&gt;count == 0)//无联系人情况
        {
                printf("无联系人可查找\n");
                return;
        }
        printf("请输入要查找的人的姓名\n");
        char arr = { 0 };
        scanf("%s", arr);
        int a = find_by_name(con, arr);//套用查找函数
        if (a == -1)
        {
                printf("要查找的人不存在\n");
                return;
        }
        printf("%-20s\t%-5s\t%-5s\t%-12s\t%-30s\n", "姓名", "年龄", "电话", "性别", "地址");
        printf("%-20s\t%-5d\t%-5s\t%-12s\t%-30s\n",
                        con-&gt;data.name,
                        con-&gt;data.age,
                        con-&gt;data.tele,
                        con-&gt;data.sex,
                        con-&gt;data.addr);//显示部分
}



//删除
void del_contact(contact* con)
{
        static int find_by_name(const contact * con, const char* arr);//函数声明
        assert(con);//断言防止空指针
        char arr = { 0 };
        if (con-&gt;count == 0)//无联系人情况
        {
                printf("无联系人可删除\n");
                return;
        }
        printf("请输入要删除人的名字\n");
        scanf("%s", arr);
        int a = find_by_name(con,arr);//函数回调
        if (a == -1)
        {
                printf("要删除的人不存在\n");
                return;
        }
        for (; a &lt; con-&gt;count - 1; a++)//删除本质就是把后面的信息提前,覆盖掉这个下标的信息
        //当然也还有其他的写法
        {
                con-&gt;data = con-&gt;data;
        }
        con-&gt;count--;
        printf("删除成功\n");
}


//修改函数
//修改也要先找

void modify_contact(contact* con)
{
        static int find_by_name(const contact * con, const char* arr);//函数声明
        assert(con);//断言防止空指针
        if (con-&gt;count == 0)
        {
                printf("无联系人可修改\n");//没有的情况判断
                return;
        }
        printf("请输入要修改的人的姓名\n");
        char arr = { 0 };
        scanf("%s", arr);
        int a = find_by_name(con, arr);//调用函数
        if (a == -1)
        {
                printf("要修改的人不存在\n");
                return;
        }
        printf("请输入修改姓名\n");
        scanf("%s", con-&gt;data.name);
        printf("请输入修改年龄\n");
        scanf("%d", &amp;(con-&gt;data.age));
        printf("请输入修改电话\n");
        scanf("%s", con-&gt;data.tele);
        printf("请输入修改性别\n");
        scanf("%s", con-&gt;data.sex);
        printf("请输入修改地址\n");
        scanf("%s", con-&gt;data.addr);
        printf("修改成功\n");//信息的修改
}


//排序函数
//这里我使用了qsort
static int cmp_peo_by_name(const void* e1, const void* e2)//qsort调用的排序函数
{
        return strcmp(((const peo*)e1)-&gt;name, ((const peo*)e2)-&gt;name);
}

void sort_contact(contact* con)//本体
{
        static int cmp_peo_by_name(const void* e1, const void* e2);//函数调用
        assert(con);//断言
        if (con-&gt;count == 0)
        {
                printf("无联系人可排序\n");//没人的情况
                return;
        }
        printf("正在按姓名排序\n");
        qsort(con-&gt;data, con-&gt;count, sizeof(peo), cmp_peo_by_name);//qsort排序
        printf("排序成功\n");
        show_contact(con);//这里不声明是因为这些函数都会在头文件中声明,但内些静态函数不会
}

//销毁,保存,清空
void destroycontact(contact* con)//释放内存
{
        assert(con);
        free(con-&gt;data);
        con-&gt;data = NULL;
}


void save_contact(const contact* con)//保存信息
{
        assert(con);
        FILE* p = fopen("contact.txt", "wb");
        if (p == NULL)
        {
                perror("保存失败:save_contact:fopen");
                return;
        }
        fwrite(con-&gt;data, sizeof(peo), con-&gt;count, p);
        fclose(p);
        p = NULL;
}

void clear_contact(contact* con)//清空文件信息和当前结构体信息
{
        assert(con);
        FILE* p = fopen("contact.txt", "w");
        if (p == NULL)
                return;
        fclose(p);
        p = NULL;
        con-&gt;count = 0;
        memset(con-&gt;data,0, sizeof(peo));
        printf("已全部清除\n");
}

</code></pre>
<p>最后我们再在头文件中声明一下这些函数,为了调用</p>
<pre><code>//初始化通讯录
void init_contact(contact* con);

//增加联系人
void add_contact(contact* con);

//显示
void show_contact(contact* con);

//删除
void del_contact(contact* con);

//查找
void search_contact(contact* con);

//修改
void modify_contact(contact* con);

//排序
void sort_contact(contact* con);

//退出后内存销毁
void destroycontact(contact* con);

//退出后的保存
void save_contact(const contact*con);

//清空通讯录
void clear_contact(contact*con);
</code></pre>
<p>静态函数不在头文件声明,主要和一些商业习惯有关</p>
<p>好了,这样就结束了,比较简陋,旨在练手,大佬勿喷</p><br><br>
来源:https://www.cnblogs.com/827-s/p/19284329
頁: [1]
查看完整版本: 用C语言和文本文件实现一个简单的,可保存的通讯录