【实验报告】高软实验7

来源:互联网 发布:写算法 编辑:程序博客网 时间:2024/05/29 18:46

【Jerryykt1464929958440 + 《软件工程(C编码实践篇)》MOOC课程作业http://mooc.study.163.com/course/USTC-1000002006 】

github地址:https://github.com/JerryLittleBear/experiment.git


【实验目的】:

1. 为menu子系统设计接口,并写用户范例代码来实现原来的功能

2. 使用make和make clean来编译程序和清理自动生成的文件

3. 使menu子系统支持带参数的复杂命令,并在用户范例代码中自定义一个带参数的复杂命令

4. 使用getopt()函数来获取命令行参数


【实验过程】:

1. menu子系统接口

定义menu.h接口

#ifndef __MENU_H__#define __MENU_H__/*----------Add Command to Menu----------*/int MenuConfig(char* cmd, char* desc, int (*handler)(int argc, char** argv));/*----------Menu Engine Execute----------*/int ExecuteMenu();int Help(int argc, char **argv);int Quit(int argc, char **argv);#endif
修改menu.c中实现接口:
int ExecuteMenu(){    while(1)    {int argc = 0;char *argv[CMD_MAX_ARGV_NUM];        char cmd[CMD_MAX_LEN];        char *pcmd = NULL;        printf("Input a cmd number > ");        /*scanf("%s", cmd);*/        pcmd = fgets(cmd, CMD_MAX_LEN, stdin);        if(pcmd == NULL)        {            continue;        }        /* convert cmd to argc/argv */        pcmd = strtok(pcmd, " ");        while(pcmd != NULL && argc < CMD_MAX_ARGV_NUM)        {            argv[argc] = pcmd;            argc++;            pcmd = strtok(NULL, " ");        }        if(argc == 1)        {            int len = strlen(argv[0]);            *(argv[0] + len - 1) = '\0';        }        tDataNode *p = (tDataNode*)SearchLinkTableNode(head, SearchCondition, (void*)argv[0]);        if(p == NULL)        {            printf("This is a wrong cmd!\n");            continue;        }        if(p->handler != NULL)        {            p->handler(argc, argv);        }    }    return 0;}int MenuConfig(char* cmd, char* desc, int (*handler)(int argc, char** argv)){    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;
2. Makefile
参考老师给的Makefile

CC_PTHREAD_FLAG      = -lpthreadCC_FLAGS             = -cCC_OUTPUT_FLAGS      = -oCC                   = gccRM                   = -rmRM_FLAGS             = -fSRC     =  linktable.c menu.c test.cTARGET  =  testOBJS    =  linktable.o menu.o test.oall:$(OBJS)$(CC) $(CC_OUTPUT_FLAGS) $(TARGET) $(OBJS).c.o:$(CC) $(CC_FLAGS) $<clean:$(RM) $(RM_FLAGS) $(OBJS) $(TARGET) *.bak
对代码进行make操作


3. 实现带参数的复杂命令

int Hello(int argc, char** argv){    const char * optString = "abc";    opterr = 0;    int opt;    while((opt = getopt(argc, argv, optString)) != -1)    {        switch(opt)        {            case 'a':                printf("method a\n");                break;            case 'b':                printf("method b\n");                break;            default:                break;        }    }    optind = 0;    return 0;}
可以实现对命令的带参数操作:

命令是hello

-a和-b参数可以实现不同的hello命令



【实验总结】:

1、子系统的设计必须根据项目、功能的不同而不同,必须进行充分的需求分析,不可盲目。
就比如我们要为menu程序设计接口就不能和为linktable设计的接口一模一样,而必须考虑menu需要提供的功能,只需定义两个接口函数:MenuConfig用来给菜单加入命令,ExecuteMenu用来启动menu的引擎.
2、利用Makefile进行自动化编译
3、利用main函数的argc和argv参数,实现对命令行中带参数命令的实现

#include <stdio.h>//#包含<stdio.h>
 
int main(int argc,char* argv[])    //整数类型主函数(整数类型统计参数个数,字符类型指针数组指向字符串参数)
{
    printf("%d\n",argc);           //格式化输出
    while(argc)                    //当(统计参数个数)
        printf("%s\n",argv[--argc]);   //格式化输出
    return 0;                      //返回0;正常退出
}

4、strtok函数的使用
char *strtok(char s[], const char *delim);
strtok()用来将字符串分割成一个个片段。参数s指向欲分割的字符串,参数delim则为分割字符串,当strtok()在参数s的字符串中发现到参数delim的分割字符时则会将该字符改为\0 字符。在第一次调用时,strtok()必需给予参数s字符串(此时返回分割符前面的字符串),往后的调用则将参数s设置成NULL,值得注意的是,如果参数是NULL,则strtok返回s字符串的第二块被分割出来的子字符串。每次调用成功则返回被分割出片段的指针,当没有被分割的串时则返回NULL。
例子:
char s1[] = "my name is jerry";
char* p1 = strtok(s1, " ");
while(p1 != NULL)
{
    printf("%s\n", p1);
    p1 = strtok(NULL, " ");
}
这样的话依次打印my、name、is、jerry等4个字符串

5、getopt函数:
int getopt(int argc,char * const argv[ ],const char * optstring);
短参数的定义
       getopt()使用optstring所指的字串作为短参数列表,象"1ac:d::"就是一个短参数列表。短参数的定义是一个'-'后面跟一个字母或数字,象-a, -b就是一个短参数。每个数字或字母定义一个参数。
  其中短参数在getopt定义里分为三种:
  1. 不带值的参数,它的定义即是参数本身。
  2. 必须带值的参数,它的定义是在参数本身后面再加一个冒号。
  3. 可选值的参数,它的定义是在参数本身后面加两个冒号 。
  在这里拿上面的"1ac:d::"作为样例进行说明,其中的1,a就是不带值的参数,c是必须带值的参数,d是可选值的参数。
  在实际调用中,'-1 -a -c cvalue -d', '-1 -a -c cvalue -ddvalue', '-1a -ddvalue -c cvalue'都是合法的。这里需要注意三点:
  1. 不带值的参数可以连写,象1和a是不带值的参数,它们可以-1 -a分开写,也可以-1a或-a1连写。
  2. 参数不分先后顺序,'-1a -c cvalue -ddvalue'和'-d -c cvalue -a1'的解析结果是一样的。
  3. 要注意可选值的参数的值与参数之间不能有空格,必须写成-ddvalue这样的格式,如果写成-d dvalue这样的格式就会解析错误。

返回值
   getopt()每次调用会逐次返回命令行传入的参数。
   当没有参数的最后的一次调用时,getopt()将返回-1。
    当解析到一个不在optstring里面的参数,或者一个必选值参数不带值时,返回'?'。
   当optstring是以':'开头时,缺值参数的情况下会返回':',而不是'?' 。
参考资料:
http://vopit.blog.51cto.com/2400931/440453
http://www.cnblogs.com/qingergege/p/5914218.html
http://blog.csdn.net/huangxiaohu_coder/article/details/7475156


6、char* fgets(char* buf, int bufsize, FILE* stream)函数,每次读取最多bufsize-1个字符,回车结束,回车会读入,并且最后附加一个'\0',相当与在回车后面加一个'\0'

原创粉丝点击