2017 Fall Software Engineering Learning (6)

2017 Fall Software Engineering Learning (6)

模块化 命令行菜单

公开发表一篇实验报告,并在实验报告中注明【网易云课堂昵称 + 《软件工程(C编码实践篇)》MOOC课程作业http://mooc.study.163.com/course/USTC-1000002006


一、实验需求分析

  • 为menu子系统设计接口,并写用户范例代码来实现原来的功能;
  • 使用make和make clean来编译程序和清理自动生成的文件;
  • 使menu子系统支持带参数的复杂命令,并在用户范例代码中自定义一个带参数的复杂命令;
  • 可以使用getopt函数获取命令行参数

二、程序思路分析

我们在上次的lab5代码的基础上进行修改(如果不清楚上次lab5实现了什么可以看看我之前的实验5)。
首先,我们将menu模块化,这就意味着我们将原来的main函数的内容用子函数实现并且定义接口,同时由于原来的menu程序是在menu.c文件里面定义的函数具体操作,所以说修改命令的具体内容就要在menu.c文件里面。但是为了向用户隐藏具体的实习细节,我们希望用户能够不要操作我们提供的源文件。那么我们就还需要一个函数来添加命令,用户能在自己的main函数里面调用这个函数向menu模块添加命令。

实现了上述功能之后就是让我们的命令支持参数输入,就像之前的main函数一样。这里我们只用修改一下menu模块,对于输入的字符串,我们用strtok解析一下,然后传递给相应的函数handler就可以了。

下面是实现上述功能的子函数模块:

int MenuConfig(char *cmd, char *desc, int (*handler)())
//为命令链表添加命令
int GetOpt(char *pcmd, int *argc, char *argv[])
//从pcmd这一输入的字符串中将命令、参数提取出来
int ExecuteMenu()
//menu主模块,原来的main函数

三、程序具体实现

代码量有点小多,所以我就不贴上来啦。这里就介绍一下主要的几个函数模块,其他内容与实验5 的内容一样

int MenuConfig(char *cmd, char *desc, int (*handler)())
{
    tDataNode* pNode = NULL;
    if (head == NULL)
    {
        head = CreateLinkTable();
        pNode = (tDataNode*)malloc(sizeof(tDataNode));
        pNode->cmd = "help";
        pNode->desc = "Menu List:";
        pNode->handler = Help;
        AddLinkTableNode(head,(tLinkTableNode *)pNode);
    }
    pNode = (tDataNode*)malloc(sizeof(tDataNode));
    pNode->cmd = cmd;
    pNode->desc = desc;
    pNode->handler = handler;
    AddLinkTableNode(head,(tLinkTableNode *)pNode);
    return 0;
}

给命令列表添加命令的模块。

nt GetOpt(char *pcmd, int *argc, char *argv[])
{
    if (pcmd == NULL || argc == NULL || argv == NULL)
    {
        printf("Wrong in GetOpt, invalid arguments\n");
        return FAILURE;
    }

    pcmd = strtok(pcmd," ");
    while(pcmd != NULL && *argc < CMD_MAX_ARGV_NUM)
    {
        argv[*argc]= pcmd;
        (*argc)++;
        pcmd = strtok(NULL," ");
    }
    if (*argc >= 1)
    {
        *(argv[*argc-1]+strlen(argv[*argc-1])-1)='\0';
    }

    return SUCCESS;
}

函数从pcmd中提取命令和参数,将参数数目存在argc中,参数具体内容存在字符串数组argv中。注意argc要用指针,不然无法将内容传递出去。

int ExecuteMenu()
{
    //InitMenuData(&head); 
   /* cmd line begins */
    char cmd[CMD_MAX_LEN];

    while(1)
    {
        int argc = 0;
        char *argv[CMD_MAX_ARGV_NUM];
        char *pcmd = NULL;
        printf("Input a cmd number > ");
        //scanf("%s", cmd);
        pcmd = fgets(cmd, CMD_MAX_LEN, stdin);
        if(pcmd == NULL)
        {
            continue;
        }

        if (GetOpt(pcmd, &argc, argv) == FAILURE)
        {
            printf("Wrong in ExecuteMenu, GetOpt failure\n");
            return FAILURE;
        }

        tDataNode *p = FindCmd(head, cmd);
        if( p == NULL)
        {
            printf("This is a wrong cmd!\n ");
            continue;
        }
        printf("%s - %s\n", p->cmd, p->desc); 
        if(p->handler != NULL) 
        { 
            p->handler(argc,argv);
        }

    }

    return SUCCESS;
}

就是命令行菜单的主函数,其他的与实验5 区别不大。这里主要就时调用了GetOpt来解析参数,而且将gets换成了fgets,这是因为gets有数组超界的隐患,可能会导致内存泄漏。
接下来就是我的程序了:

/*******************************************************************************
 * Author    : acrididcheng
 * Email     : chenghuaming@aliyun.com
 * First Created : 2017-11-04 23:32
 * Filename  : test.c
 * Description   : 
 * *****************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "menu.h"
#include "linktable.h"

int Quit(int argc, char *argv[])
{
    if(argc != 2)
    {
        printf("Usage: quit [-h/-t]\n");
        return FAILURE;
    }
    if (strcmp(argv[1],"-h") == 0)
    {
        ;
    }
    if (strcmp(argv[1],"-t") == 0)
    {
        printf("Bye~~^_^\n");
    }
    exit(0);
}

int Operate(int argc, char *argv[])
{
    if (argc != 4)
    {
        printf("Usage: operate ['+'/'-'/'*'/'/'] [double1] [double2]\n");
        return FAILURE;
    }

    if (strcmp(argv[1],"+") == 0)
    {
        printf("the output is:%f\n",(atof(argv[2])+atof(argv[3])));
        return SUCCESS;
    }
    else if (strcmp(argv[1],"-") == 0)
    {

        printf("the output is:%f\n",(atof(argv[2])-atof(argv[3])));
        return SUCCESS;
    }
    else if (strcmp(argv[1],"*") == 0)
    {
        printf("the output is:%f\n",(atof(argv[2])*atof(argv[3])));
        return SUCCESS;
    }
    else if (strcmp(argv[1],"/") == 0)
    {
        printf("the output is:%f\n",(atof(argv[2])/atof(argv[3])));
        return SUCCESS;
    }
    else
    {
        printf("wrong operation\n");
        return FAILURE;
    }
    return FAILURE;
}

int main()
{
    MenuConfig("version","menu Independent version 1.0.",NULL);
    MenuConfig("quit","quit from the menu",Quit);
    MenuConfig("operate","do some math calculation",Operate);
    ExecuteMenu();

    return 0;
}

可以看见我写了一个quit()函数和一个operate()函数。
这里为了测试功能,我将quit变成了加参数才能退出的函数,而且添加了一个具体的功能命令–operate,来实现double的基本运算(+,-,*,/)。
这个程序我就不讲了,超简单。有兴趣就自己看一下代码,很短。

哦,还有最后一个,要写一个Makefile文件,这个我在之前的几个实验里面都有用过,在写实验2的时候有过较详细的介绍。这个Makefile的代码变种特别多,所以这里就不再介绍了。有兴趣的可以去make的wiki看看各种语法。

四、程序效果演示

这里写图片描述
make一下后就可以运行了,这里可以发现,功能还是没什么大问题的。
运行完后make clean 清理一下就行了:
这里写图片描述

五、实验总结

这次讲解了参数的处理和整体模块化,让整个程序完整了,符合了尽可能模块化,精简main函数的思想,而且这次用到了makefile,使得整个工程圆满结束。非常有意义!!


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值