2017 Fall SoftwareEngineering Learning (6)

来源:互联网 发布:windows ant下载 编辑:程序博客网 时间:2024/05/18 00:40

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,使得整个工程圆满结束。非常有意义!!


原创粉丝点击