【问题标题】:How to handle C command line argument flags in a neater way?如何以更简洁的方式处理 C 命令行参数标志?
【发布时间】:2020-11-23 13:11:33
【问题描述】:

我有一小段 C 代码,它接受一组命令行参数 --help-h-d-o(每个对应代表“帮助”、“十六进制”、“十进制", "八进制"),我根据传递的参数调用某些函数,-h 将调用hexaFlag()-dh 将调用hexaFlag()decFlag()。但是,为了做到这一点,我使用了一个混乱的if else 块。有没有更简单的方法来实现这一点?有人告诉我使用switch 语句,但考虑到我每次都检查不同的条件,我不知道如何在这里使用它。

main()我指的代码的函数:

int main(int argc, char* argv[]){

        if(argc == 1){
                printf("%s",usage());
                printf("Use --help for more options.\n");
        }
        else if(strcmp(argv[1], "--help") == 0){
                printf("%s", usage());
                printf("Options are:\n  -h = Hexadecimal values\n  -d = Decimal values\n  -o = Octal values\n  --help = Shows this message\n");
        }
        else if(strchr(argv[1], '-') != NULL){
                if(strchr(argv[1], 'h') != NULL){
                        hexaFlag(argc, argv);
                }
                if(strchr(argv[1], 'd') != NULL){
                        decFlag(2, argc, argv);
                }
                if(strchr(argv[1], 'o') != NULL){
                        octaFlag(argc, argv);
                }
        }
        else {
                decFlag(1, argc, argv);
        }
        return 0;
}

【问题讨论】:

  • 你可以看看getopt()example of getopt()
  • 关于getopt(以及您想要的getopt_long),有一些可移植的开源实现可以在您自己的代码中使用。如果您稍微搜索一下,还有 大量 其他库可以帮助解析命令行选项和参数。
  • 首先,从main中取出选项解析。在一个接受struct args * 并设置其值的函数中进行所有解析。

标签: c if-statement switch-statement command-line-arguments


【解决方案1】:

我认为您应该稍微修改函数签名以使它们保持一致。这将允许您执行以下操作:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <libgen.h>

void hexaFlag(int x, int argc, char **argv) { printf("%d:%s\n", x, __func__); }
void decFlag(int x, int argc, char **argv)  { printf("%d:%s\n", x, __func__); }
void octaFlag(int x, int argc, char **argv) { printf("%d:%s\n", x, __func__); }

struct args {
        void (*f)(int, int, char **);
        int x;
};

void
parse_args(int argc, char *argv[], struct args *A) 
{
        int c;
        if( argc == 1 || !strcmp(argv[1], "--help") ){
                printf("usage: %s [-hdo] [--help] arg [arg...]\n",
                        basename(argv[0]));
                exit(EXIT_SUCCESS);
        }
        A->f = decFlag;
        A->x = 1;

        while( (c = getopt(argc, argv, "hdo")) != -1 ) {
                switch( c ){
                case 'h': A->f = hexaFlag; break;
                case 'd': A->f = decFlag; A->x = 2; break;
                case 'o': A->f = octaFlag; break;
                default:  exit(EXIT_FAILURE);
                }
        }
}

int
main(int argc, char* argv[])
{
        struct args A;
        parse_args(argc, argv, &A);
        A.f(A.x, argc, argv);
        return 0;
}

您可能不应该将 argc/argv 直接传递给工作函数,而应该从它们中提取您需要的内容到 parse_args() 中的 struct args 中。

【讨论】:

    【解决方案2】:

    我的基本开关处理(示例),非常实用但可能并不美观,是:

    int main (int argc, char *argv[])
    {
      int i=0, number, files=0;
    
      /* process switches;  other prms are considered files  (2)
         that are opened for input/output.  Files not  specified
         are taken as stdin and stdout. Prms can be in any order.
      */
    
      while (++i < argc)
        switch (argv[i][0]) {
          case '-': while (*++argv[i])
                      switch (*argv[i]) {
                      case 'N':
                            ++argv[i]; number= 0;
                            while (isdigit(*argv[i]))
                                number = number *10 + *argv[i]++ - '0';
                            argv[i]--;
                            break;
    
                        case 'P' : printf ("Prm: P\n"); break;
                        case 'O' : printf ("Prm: O\n"); break;
                        case 'o' : printf ("Prm: o\n"); break;
                      default :
                        printf ("Bad switch %c, ignored.\n",*argv[i]);
                      }
                    break;
    
          default :
            switch (files) {
            case 0: if ((inf=fopen(argv[i],"r")) == 0)
                        pexit("Error opening input file %s.", argv[i]);
                    files++; break;
            case 1: if ((outf=fopen(argv[i],"w")) == 0)
                        pexit ("Error creating output file %s.", argv[i]);
                    files++; break;
            case 2: fprintf (stderr,"Too many file arguments: %s ignored.\n",argv[i]);
                    break;
            } /* end switch files */
          } /* end switch argc */
    
        if (files <1) inf = stdin;
        if (files <2) outf = stdout;
    

    【讨论】:

    • 如果fopen 由于权限问题而失败,Input file %s not found 是一个误导性错误消息。不要猜测错误的原因。
    • @WilliamPursell - 消息已修复。你喜欢剩下的吗?
    • 只要pexit 添加系统错误,这似乎没问题,但是您将在消息和系统错误之间换行,这看起来很奇怪。 IMO 最好将系统错误与消息的其余部分放在同一行。 (例如,perror 的行为但似乎“文件参数过多”应该写入标准错误。
    • @WilliamPursell,也解决了这个问题。然而,美丽(或不赞成)当然正在处理中。
    猜你喜欢
    • 2012-10-20
    • 2013-01-22
    • 2013-05-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-25
    相关资源
    最近更新 更多