【问题标题】:Parsing non-standard arguments解析非标准参数
【发布时间】:2022-02-05 08:48:43
【问题描述】:

我正在尝试创建一个程序,用一个或多个指定字符替换源文件中的一个或多个字符,并将新文本写入目标文件。

例如,以下对程序“sub”的调用都会导致将 src.txt 中的 'a' 和 'b' 实例分别替换为 'x' 和 'y' 并将其写入 dest.txt。

$ sub --ab -+xy -i src.txt -o dest.txt 
$ sub -i src.txt -o dest.txt --ab -+xy
$ sub -o dest.txt --ab -i src.txt -+xy
$ sub --ab -o dest.txt -+xy -i src.txt 

我查看了 C 的 getopt(),但我不认为它涵盖了选项后面可能的多个字符。

程序接受参数的方式是固定的。我将如何解析这些参数,在某些情况下,文本文件中可能有多个字母要替换?并处理任何参数排序?

我无法打开字符串,但我无法使用选项中的特殊字符创建枚举。据我所知 getopt() 不能处理我的程序期望参数的方式。所以我留下了以下非常不完整的代码:

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

    // help message displayed for "sub" and "sub -h"
    if (argc == 1 || strcmp(argv[1], "-h") == 0){
        helpMsg();
    } else {
        // process rest of argv
        int i = 2;
        while (argc != 0) {

            char *opt = argv[i];

            switch(opt){

            }

            i++;
            argc--;
        }
    }

    return 0;
}

【问题讨论】:

  • 我将如何解析这些论点 - 好吧......手动,如果你想不出办法让它getopt-friendly。
  • 只需使用argcargv。但我强烈建议不要使用奇怪的非标准方式来传递参数。不要惹恼用户。
  • --ab-+xy 会让任何长期使用 Linux 的用户感到困惑。或者至少,他们让我感到困惑。这不仅是非标准语法,而且公然违反了- 用于短选项而-- 用于长选项的约定。根据该约定,--ab 应该是一个名为“ab”的选项,-+xy 应该是 -+ -x -y 的简写。
  • 如您之前所说,您将不得不设置一些变量——用于输入文件、输出文件、搜索字符和替换字符。然后,您遍历传递给程序的每个参数,检查它包含的内容并适当地翻译它。 -+-- 符号使这令人讨厌且不标准,但大概这不是你的错,你的老师希望你必须手动完成这项工作。你是对的,(POSIX)getopt() 和任何标准变体都不会处理这种表示法——这就是“非标准”在这种情况下的含义。

标签: c command-line-arguments


【解决方案1】:

虽然它非常不正统,但这段代码可以在 Mac 上运行:

#include <stdio.h>
#include <unistd.h>

int main(int argc, char **argv)
{
    int opt;
    while ((opt = getopt(argc, argv, "i:o:+:-:")) != -1)
    {
        switch (opt)
        {
        case '+':
        case '-':
        case 'i':
        case 'o':
            printf("Got '-%c' argument '%s'\n", opt, optarg);
            break;
        default:
            printf("!! FAIL !! optopt = %c\n", optopt);
            break;
        }
    }
    return 0;
}

它告诉getopt() - 是一个需要参数的选项“字母”(字符),而+ 是一个需要参数的选项字符。我可以这样运行(getopt23.c 中的代码编译为getopt23):

$ ./getopt23 -i input -o output --ab -+xy
Got '-i' argument 'input'
Got '-o' argument 'output'
Got '--' argument 'ab'
Got '-+' argument 'xy'
$

请注意,-i 和输入文件等之间的强制空格对于此代码不是强制性的:

$ ./getopt23 -iinput -ooutput -+ xy --ab
Got '-i' argument 'input'
Got '-o' argument 'output'
Got '-+' argument 'xy'
Got '--' argument 'ab'
$ ./getopt23 -iinput -ooutput -+ xy -- ab
Got '-i' argument 'input'
Got '-o' argument 'output'
Got '-+' argument 'xy'
$

这两个中的第二个很有趣——-- 表示选项的结尾,ab 是非选项参数(通常是文件名)。如果代码被循环扩展:

for (int i = optind; i < argc; i++)
    printf("Plain argument %d: '%s'\n", i, argv[i]);

然后ab(但不是--)将被打印为“普通参数”。 (POSIX Utility Syntax Guidelines 使用名称“操作数”作为我所说的“非选项参数”。)

如果您编写自己的代码,则可以强制执行“与选项分开的文件名”和“附加到---+ 选项的替换字符串”。使用常规的 getopt(),要做到这一点而不引起未定义的行为非常困难。


我之前commented那个:

你说得对,(POSIX)getopt() 和任何标准变体都不会处理这种表示法——这就是“非标准”在这种情况下的含义。

我必须部分撤回该声明。如果您必须将-i 和输入文件名放在单独的参数中,并且不显示为-iinput,并且与-o 和输出文件名类似,并且如果字符集必须附加到-- 和@ 987654346@,那么你不能可靠地使用getopt()。如果该表示法可以灵活使用,那么您毕竟可以使用getopt(),而我之前的评论是夸大其词。

【讨论】:

    【解决方案2】:

    有时最简单的事情就是在循环中初始化您的选项,针对每种情况进行测试。

    #include <iso646.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    int help_quit( const char * message )
    {
      printf( "%s\n", message );
      exit( 0 );
    }
    
    int main( int argc, char ** argv )
    {
      FILE       * infile       = NULL;
      FILE       * outfile      = NULL;
      const char * in_filename  = NULL;
      const char * out_filename = NULL;
      const char * to_remove    = "";
      const char * to_add       = "";
      
      for (int n = 1;  n < argc;  n += 1)
      {
        // All arguments must begin with '-'
        if (argv[n][0] != '-') 
        {
          help_quit( "All arguments must begin with a dash" );
        }
        
        // -h, --help
        if ((strcmp( argv[n], "-h" ) == 0) or (strcmp( argv[n], "--help" ) == 0))
        {
          help_quit( "usage:\n  sub ..." );
        }
        
        // -i FILENAME
        if (strcmp( argv[n], "-i" ) == 0)
        {
          if (argc-n < 2) help_quit( "missing input filename" );
          in_filename = argv[++n];
          continue;
        }
        
        // -o FILENAME
        if (strcmp( argv[n], "-o" ) == 0)
        {
          if (argc-n < 2) help_quit( "missing output filename" );
          out_filename = argv[++n];
          continue;
        }
        
        if (argv[n][1] == '-')
        {
          to_remove = argv[n] + 2;
          continue;
        }
        
        if (argv[n][1] == '+')
        {
          to_add = argv[n] + 2;
          continue;
        }
        
        help_quit( "unknown option" );
      }
      
      // validate args
      if (strlen( to_remove ) != strlen( to_add )) help_quit( "number of remove chars != number of add chars" );
      if (!in_filename)  help_quit( "you must specify an input filename" );
      if (!out_filename) help_quit( "you must specify an output filename" );
      if (!(infile  = fopen( in_filename,  "r" ))) help_quit( "could not open input file" );
      if (!(outfile = fopen( out_filename, "w" ))) 
      {
        fclose( infile );
        help_quit( "could not open output file" );
      }
      
      // do stuff
      printf( "%s\n", "TO DO" );
      
      // clean up
      fclose( outfile );
      fclose( infile );
      return 0;
    }
    

    【讨论】:

      猜你喜欢
      • 2012-01-29
      • 2011-12-08
      • 2012-10-20
      • 1970-01-01
      • 1970-01-01
      • 2017-04-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多