模仿现实生活中的通讯录(2)

news2024/11/24 20:55:02

距离第一篇已经过去很久,我之所以暂时放下通讯录,是因为学业颇多,无暇顾及。现在放假已经有一段时间了,脱离每天忙碌的生活后,我只想享受一下整日无事,浑浑噩噩过一天的感觉,只不过差点没收得住。前段时间是期末考试周,除去复习以及完成一些报告书之外,每天没有别的的任务,这样的情况下,我自然是想过打开通讯录,继续编辑的,但终归还是懒了点,一直到24号,回国后,又睡了一夜,我才开始修改代码。

学习了一段时间,能力不会不涨的,现在回看自己以前所写,真的感觉有很多漏洞。我首先就修改了变量的命名,比如那些拼音式的命名。其次又去掉了几个全局变量,以及不需要的代码,比如虚拟回收站,下面会写缘由。最后还要更改实现功能的代码。除去基本框架留存,每个具体功能都需要想清楚是否会联系到一起,以及可能要cv一下其他代码。

接下来我会记录一下现在的改动,之后有新方法时再写博客。

针对通讯录要实现的功能,我添加了分类功能,具体思路还没有,但大概会做一个固定分类形式的代码。

先看框架

 

 

 Book.c

void menu()
{
    printf("\n");
    printf("*************************************\n");
    printf("*******   1. 增加   2. 删除   *******\n");
    printf("*******   3. 查找   4. 修改   *******\n");
    printf("*******   5. 展示   6. 排序   *******\n");
    printf("*******   7. 分类   8. 保存   *******\n");
    printf("*******        0. 退出        *******\n");
    printf("*************************************\n");
    printf("\n");
}

enum Options
{
    Exit,
    Add,
    Del,
    Find,
    Modify,
    Show,
    Sort,
    //Classify,
    Save,
};

int main()
{
    int input = 0;
    Contact book;
    InitContact(&book);
    LoadContact(&book);
    do
    {
        menu();
        printf("请选择: >");
        scanf("%d", &input);
        switch (input)
        {
        case Add:
            AddContact(&book);
            break;
        case Del:
            DelContact(&book);
            break;
        case Find:
            FindContact(&book);
            break;
        case Modify:
            ModifyContact(&book);
            break;
        case Show:
            ShowContact(&book);
            break;
        case Sort:
            SortContact(&book);
            break;
        /*case Classify:
            ClassifyContact(&book);
            break;*/
        case Save:
            SaveContact(&book);
            break;
        case Exit:
            SaveContact(&book);
            DestroyContact(&book);
            printf("退出通讯录\n");
            break;
        }
    } while (input);
    return 0;
}

在初始化book时,原本是只调用InitContact,LoadContact放在Init里面。我把Load拿了出来,两个各司其职,是为了之后要再开一个结构体,那么就可以直接初始化而不调用Load。

Contact.h

#define  _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>

#define MAX 100
#define NAME 20//姓名
#define GENDER 7//性别
#define ADDRESS 20//地址
#define WORK 10//职业
#define NUMBER 15//电话
#define SIZE 30//初始化时动态开辟的空间
#define ADD_SZ 10//增容时所用a

typedef struct PeoInfo
{
    char name[NAME];
    char gender[GENDER];
    char address[ADDRESS];
    char work[WORK];
    char number[NUMBER];
    int age;
}PeoInfo;

typedef struct Contact
{
    PeoInfo* data;
    int sz;
    int capacity;
}Contact;

void InitContact(Contact* ps);

void LoadContact(Contact* ps);

void AddContact(Contact* ps);

void DelContact(Contact* ps);

void FindContact(Contact* ps);

void SortContact(Contact* ps);

void ModifyContact(const Contact* ps);

void ShowContact(const Contact* ps);

//void ClassifyContact(const Contact* ps);

void DestroyContact(Contact* ps);

void SaveContact(Contact* ps);

现在看具体实现内容

最终实现的通讯录要给人看,所以用来删除的回收站和写入的通讯录文件都用格式化输入和输出函数,fprintf   fscanf。以及另建一个通讯录,和主通讯录一样内容,不过用二进制形式存储,在调起程序初始化的时候电脑从这个文件读取内容。

void LoadContact(Contact* ps)
{
    FILE* pf = fopen("E://Computer.txt", "rb");
    if (pf == NULL)
    {
        perror("LoadContact::fopen");
        return;
    }
    PeoInfo ptr = { 0 };
    while (fread(&ptr, sizeof(PeoInfo), 1, pf))
    {
        //检查是否要增容
        check_capacity(ps);
        ps->data[ps->sz] = ptr;
        ps->sz++;
    }
    fclose(pf);
    pf = NULL;
}

void InitContact(Contact* ps)
{
    assert(ps);
    ps->data = (PeoInfo*)malloc(SIZE * sizeof(PeoInfo));
    if (ps->data == NULL)
    {
        perror("InitContact()");
        return;
    }
    ps->sz = 0;
    ps->capacity = SIZE;
}

增加

还是一样

这里其实也可以边加边放入通讯录文件中,也可以在退出时一起保存,两者都可。

void AddContact(struct Contact* pa)
{
    assert(pa);
    if (0 == check_capacity(pa))
    {
        return;
    }
    printf("请输入要添加的名字:>");
    scanf("%s", pa->data[pa->sz].name);
    printf("请输入要添加的性别:>");
    scanf("%s", pa->data[pa->sz].gender);
    printf("请输入要添加的电话号码:>");
    scanf("%s", pa->data[pa->sz].number);
    printf("请输入要添加的地址:>");
    scanf("%s", pa->data[pa->sz].address);
    printf("请输入要添加的职业:>");
    scanf("%s", pa->data[pa->sz].work);
    printf("请输入要添加的年龄:>");
    scanf("%d", &pa->data[pa->sz].age);
    pa->sz++;
    printf("已成功添加第%d个联系人\n", pa->sz);
}

删除

删除分为指定删除和批量删除。之前的代码做了很多变量来控制整个程序,比如接收返回的数字,程序正常进行就返回0,不正常或者出错就返回一些不同的数字,根据这些数字来安排下一步;以及检查虚拟回收站是否需要扩容,虚拟回收站出现的错误等等,以上看起来比较荒诞,但实际都是我之前所想,现在做了一些改变。

虚拟回收站去掉。虚拟回收站并不需要,在程序之外用一个文件用来做回收站,用户可以在那里看到删除过的联系人。那么虚拟回收站有什么作用?我想了想,之前大概是认为我自己调起程序后可以随时查看删除过的东西,非常方便。但程序员也可以看回收站文件啊,没必要做一个虚拟回收站,且这个东西是调起程序后才出现的,想要一直能够看到删除过的内容,那么这个虚拟回收站就需要保存好之前的记录,这点不算难,在Book.c里,运行删除功能前就建立一个结构体,调用函数时把它也传过去,就可以保存内容了,但指定和批量删除都需要虚拟回收站,为了表达回收站的大小,我们要不和上面一样,也先建立一个变量,传过去;或者在Contact.c里创建一个全局变量,这样就涉及到控制变量问题。所以这实在是多此一举。

这篇先不说批量删除功能

对于指定删除,它仍然是int类型的函数,正常运行后返回0,那么程序结束,用户再去选择功能;出现错误,返回非0整数,程序继续,如果连续3次出现错误,那就停止程序,用户重新选择功能,所以这里需要一个计数的变量。不过连续3次错误应该是很少出现的,看下面的代码图就明白了。

用户选择好要删除的联系人后,先把它放到回收站文件里,再删除

 

static int FindByName(const char name[], const struct Contact* pf)
{
    assert(name && pf);
    int i = 0;
    for (i = 0; i < pf->sz; i++)
    {
        if (0 == strcmp(pf->data[i].name, name))
        {
            return i;
        }
    }
    return -1;
}
 

static int SpecifyDel(Contact* ps)
{
    assert(ps);
    int j = 0;
    int i = 0;
    char name[NAME];
    printf("请输入要删除人的名字: >");
    scanf("%s", name);
    int m = FindByName(name, ps);
    if (m == -1)
    {
        printf("要删除的人不存在\n");
        return -1;
    }
    else
    {
        PeoInfo* n = &ps->data[m];
        FILE* bin = fopen("E://recycle bin.txt", "w+");//文件指针,往回收站文本文件里写入
        if (bin == NULL)
        {
            perror("ZDDel::fopen");
            printf("文件创建失败,请重新进行删除\n");
            return 1;
        }
        fprintf(bin, "姓名:%s  性别:%s  地址:%s  工作:%s  电话:%s  年龄:%d\n", ps->data[m].name, ps->data[m].gender, ps->data[m].address, ps->data[m].work, ps->data[m].number, ps->data[m].age);
        fclose(bin);
        bin = NULL;
        for (j = m; j < ps->sz - 1; j++)
        {
            ps->data[j] = ps->data[j + 1];
        }
        ps->sz--;
        printf("成功删除指定联系人\n");
    }
    return 0;
}

 void DelContact(Contact* pd)
{
    assert(pd);
    int n = 0;
    int s = 1;
    int b = 1;
    int i = 0;
    int num = 0;
    char ret[20] = "0";
    printf("选择批量删除前请查询可用的序列号.是否查询?\n");
    while (scanf("%s", ret) != EOF)
    {
        if (0 == strcmp("是", ret))
        {
            ShowContact(pd);
            break;
        }
        else if (0 == strcmp("否", ret))
            break;
        else
        {
            printf("请重新输入!\n");
            for (i = 0; i < 20; i++)
            {
                ret[i] = '0';
            }
            continue;
        }
    }
    printf("请选择删除模式: 1、指定删除  2、批量删除\n");
    while (scanf("%d", &n) != EOF)
    {
        if (n == 1)
        {
            while (s)
            {
                s = SpecifyDel(pd);
                if (s != 0)
                    ++num;
                if (3 == num)
                {
                    printf("程序自身已出现错误3次,强制退出,请重新开始\n");
                    s = 0;
                    num = 0;
                }
            }
            break;
        }
        else
        {
            printf("请重新输入\n");
            continue;
        }
    }
}

 

批量删除下一篇再细写。

查找、修改、展示

还是之前的代码

 

void FindContact(const struct Contact* ps)
{
    assert(ps);
    char name[NAME];
    printf("请输入要查找的名字: >");
    scanf("%s", name);
    int ret = FindByName(name, ps);
    if (ret == -1)
        printf("要查找的人不存在\n");
    else
    {
        printf("%-9s\t%-7s\t%-10s\t%-10s\t%-15s\t%5s\n", "姓名", "性别", "地址", "职业", "号码", "年龄");
        printf("%-9s\t%-7s\t%-10s\t%-10s\t%-15s\t%5d\n", ps->data[ret].name,
            ps->data[ret].gender,
            ps->data[ret].address,
            ps->data[ret].work,
            ps->data[ret].number,
            ps->data[ret].age);
    }

}

void ModifyContact(const struct Contact* ps)
{
    printf("请输入要修改人的名字:>");
    char name[NAME];
    scanf("%s", name);
    int ret = FindByName(name, ps);
    if (ret == -1)
        printf("要修改的人不存在\n");
    else
    {
        printf("请输入要修改的名字:>");
        scanf("%s", ps->data[ret].name);
        printf("请输入要修改的性别:>");
        scanf("%s", ps->data[ret].gender);
        printf("请输入要修改的电话号码:>");
        scanf("%s", ps->data[ret].number);
        printf("请输入要修改的地址:>");
        scanf("%s", ps->data[ret].address);
        printf("请输入要修改的职业:>");
        scanf("%s", ps->data[ret].work);
        printf("请输入要修改的年龄:>");
        scanf("%d", &ps->data[ret].age);
        printf("修改成功\n");
    }
}

void ShowContact(const struct Contact* ps)
{
    int i = 0;
    printf("\n");
    printf("    %-9s\t%-7s\t%-10s\t%-10s\t%-15s\t%5s\n", "姓名", "性别", "地址", "职业", "号码", "年龄");
    for (i = 0; i < ps->sz; i++)
    {
        printf("%d.  %-9s\t%-7s\t%-10s\t%-10s\t%-15s\t%5d\n", (i + 1),
            ps->data[i].name,
            ps->data[i].gender,
            ps->data[i].address,
            ps->data[i].work,
            ps->data[i].number,
            ps->data[i].age);
    }
}

保存、退出

分类之后再写。

关于保存和退出,这里我写了两个函数,但是退出里也包含了保存。保存的时候我想过将通讯录文件的内容直接复制到电脑读取的文件,但是不同格式可能无法实现,我还没细查这个问题,这里我再开了一个文件指针,保存时两个文件指针各司其职即可。

 void DestroyContact(Contact* ps)
{
    free(ps->data);
    ps->data = NULL;
    ps->capacity = 0;
    ps->sz = 0;
}

void SaveContact(Contact* ps)
{
    FILE* pb = fopen("E://Book.txt", "w");
    FILE* pc = fopen("E://Computer.txt", "wb");
    if (pb == NULL || pc == NULL)
    {
        perror("SaveContact");
        return;
    }
    int i = 0;
    for (i = 0; i < ps->sz; i++)
    {
        fprintf(pb, "姓名:%s  性别:%s  地址:%s  工作:%s  电话:%s  年龄:%d\n", ps->data[i].name, ps->data[i].gender, ps->data[i].address, ps->data[i].work, ps->data[i].number, ps->data[i].age);
        fwrite(&(ps->data[i]), sizeof(PeoInfo), 1, pc);
    }
    fclose(pb);
    fclose(pc);
    pb = NULL;
    pc = NULL;
    return;
}

下一篇再写批量删除,或者再加上分类功能。经过几次运行,现在整个程序属于正常状态。

结束。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/114679.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

leetcode150/155. 逆波兰表达式求值、最小栈;剑指 Offer 31. 栈的压入、弹出序列

目录 题目链接与简介 题目一 1.算法思路 2.总结心得 3.可执行代码 题目二 1.算法思路 2.总结心得 3.可执行代码 题目三 1.算法思路 2.总结心得 3.可执行代码 题目链接与简介 1.逆波兰表达式求值 2.最小栈 3.剑指 Offer 31. 栈的压入、弹出序列 题目一 LeetCod…

软件测试 -- 进阶 8 软件测试流程和过程

盖天下之事&#xff0c;不难于立法&#xff0c;而难于法之必行&#xff1b;不难于听言&#xff0c;而难于言之必效。-- 明 张居正 释译&#xff1a;天下大事&#xff0c;困难的不在于要制定什么法律&#xff0c;而在于立了法就一定要执行&#xff1b;重要的不在于说什么&a…

【实时数仓】DWS层之地区主题表(FlinkSQL)、关键词主题表(FlinkSQL)

文章目录一 DWS层-地区主题表(FlinkSQL)1 分组开窗和聚合计算&#xff08;1&#xff09;分组窗口&#xff08;2&#xff09;选择分组窗口的开始和结束时间戳&#xff08;3&#xff09;系统内置函数&#xff08;4&#xff09;完整代码2 将动态表转换为流&#xff0c;写入ClickHo…

计算机基础(五):C语言的程序的处理过程

一段C语言程序 打开任何一个C语言的教程&#xff0c;首先自然是展示一段 Hello World 的程序&#xff0c;类似于下面这段&#xff1a; #include <stdio.h>int main() {/* 我的第一个 C 程序 */printf("Hello, World! \n");return 0; }运行上面这段程序也很简…

Qt中的数据库(简单使用)

在Qt中支持对数据库的操作 Qt中数据库的类有&#xff1a; 驱动层&#xff1a;为具体的数据库和SQL接口层之间提供底层的桥梁SQL层&#xff1a;提供对数据库的访问 QSqlDateBase类用来创建连接QSqlQuery可以使用SQL语句实现交互用户接口层&#xff1a;实现将数据库中的数据链接…

36. 卷积神经网络(LeNet)

通过之前几节&#xff0c;我们学习了构建一个完整卷积神经网络的所需组件。 回想一下&#xff0c;之前我们将softmax回归模型和多层感知机模型应用于Fashion-MNIST数据集中的服装图片&#xff0c;为了能够应用softmax回归和多层感知机&#xff1a; 我们首先将每个大小为的图像…

【Web开发】Python实现Web服务器(Ubuntu下Flask使用MySQL数据库)

&#x1f37a;基于Python的Web服务器系列相关文章编写如下&#x1f37a;&#xff1a; &#x1f388;【Web开发】Python实现Web服务器&#xff08;Flask快速入门&#xff09;&#x1f388;&#x1f388;【Web开发】Python实现Web服务器&#xff08;Flask案例测试&#xff09;&a…

大数据-玩转数据-Kafka实战

一、kafka使用要点 要点一&#xff1a;Producer即生产者&#xff0c;向Kafka集群发送消息&#xff0c;在发送消息之前&#xff0c;会对消息进行分类&#xff0c;即Topic&#xff0c;topic1&#xff0c;topic2。Topic即主题&#xff0c;通过对消息指定主题可以将消息分类&#…

工具-能写会看正则表达式【强烈建议收藏!】

正则表达式,很常用的一个技能点,但是一般的开发流程中都是这样的: 需要验证数据上网搜一下正则表达式CV 搞定!!!今天有时间回看了一下文档,简单整理了一下里面需要注意的点,并且通过分析几个常见的正则表达式,下次遇到正则争取不再只依靠 CV 大法! 基础部分 基本语法 …

初识指针(9)

目录 1、指针是什么&#xff1f; 2、指针和指针类型 1、指针- 整数 2、指针的解引用 3、野指针 1、野指针成因 2、如何规避野指针 4、指针运算 1、指针- 整数 2、指针- 指针 3、指针的关系运算 5、指针和数组 6、二级指针 7、指针数组 1、指针是什么&#xff1f;…

04-Hystrix

服务熔断Hystrix 1. Hystrix是什么 分布式系统环境下&#xff0c;服务间类似依赖非常常见&#xff0c;一个业务调用通常依赖多个基础服务。如下图&#xff0c;对于同步调用&#xff0c;当库存服务不可用时&#xff0c;商品服务请求线程被阻塞&#xff0c;当有大批量请求调用库…

SpringBoot-2 读取properties;自动加载127个类原理总结;全部加载,按需配置

读取properties 方式一&#xff1a;非配置类填写&#xff1a;ComponentConfigurationProperties 1)建立bean&#xff1a; /只有在容器中的组件才拥有springboot提供的强大功能 Component ConfigurationProperties(prefix "mycar") public class Car {private Stri…

【机器学习】模型评估与选择

模型评估与选择 目录一、评估方法1、留出法2、交叉验证法3、自助法二、性能度量1、错误率与准确率2、查准率、查全率阈值对查准率、查全率的影响3、F1度量&#xff08;基于查准率与查全率的调和平均&#xff09;4、P-R Curve5、ROC CurvePRC和ROC的选用准则PRC和ROC的差异6、代…

python 中文转带音调的拼音

python 中文转带音调的拼音 前言python 中文转带音调的拼音1、1.1 安装pinyin模块1.2 试验1.3 效果图1.4 代码实现前言 今天整理中药材,每个药材上标上带音调的拼音,查了些,有的易形成乱码,如Shān Mi Dōnɡ,想到python自已动手转算了 python 中文转带音调的拼音 1、 …

(五)汇编语言——[bx]和loop指令

目录 [...]与&#xff08;...&#xff09; [...] &#xff08;...&#xff09; idata Loop指令 段前缀 总结 [...]与&#xff08;...&#xff09; [...] 这个我们其实见过&#xff0c;代表的是一个内存单元&#xff0c;段地址在DS中&#xff0c;偏移地址就是[bx]。 &am…

《图解TCP/IP》阅读笔记(第七章 7.4)—— RIP 路由信息协议

7.4 RIP RIP&#xff08;Routing Information Protocol&#xff0c;路由信息协议&#xff09;&#xff0c;是一种距离向量算法&#xff0c;广泛用于LAN。 该协议将路由控制信息定期&#xff08;30秒一次&#xff09;向全网广播。如果没有收到路由控制信息&#xff0c;连接就会…

【1739. 放置盒子】

来源&#xff1a;力扣&#xff08;LeetCode&#xff09; 描述&#xff1a; 有一个立方体房间&#xff0c;其长度、宽度和高度都等于 n 个单位。请你在房间里放置 n 个盒子&#xff0c;每个盒子都是一个单位边长的立方体。放置规则如下&#xff1a; 你可以把盒子放在地板上的…

筛法(线性筛,厄拉多塞筛)

在前前前前前前…的博客中,我们主要谈了欧拉筛和埃氏筛. 今天我们主要来聊一聊线性筛和厄拉多塞筛(其实和埃氏筛和欧拉筛本质上没区别哎).其实在这两种筛法中厄拉多塞筛最好懂(就连本蒟蒻一看代码就明白了…别看这个名字,容易糊弄人) 首先是厄拉多塞筛"粉墨登场"::…

HRTransNet阅读理解

E. Dual-direction short connection fusion module HRFormer applies transformer blocks to enlarge receptive field of fused feature Frs, and uses exchange units to absorb the merits of multi-scales features. The process is described as: HRFormer使用TRM块来扩…

《教育的目的》笔记——如何让学生通过树木看见森林

目录 作者简介 个人感悟 经典摘录 1、学生所受的训练应该比他们最终投身的专业更为广泛 2、学习新语言用途 3、教师的责任 4、教育的主题 5、学到的有用之物 6、教育目的 7、所有事物都不是静态的、定型的&#xff0c;而是处于形成&#xff08;becoming&#xff09;过…