【问题标题】:How shall command line arguments be passed to a function along with options in c++? [duplicate]如何将命令行参数与 c++ 中的选项一起传递给函数? [复制]
【发布时间】:2016-09-10 22:23:41
【问题描述】:

我必须通过使用命令行参数(argv、argc)在 c++ 中的函数内传递系统配置详细信息。函数如下:

  function(char * array[]){
    windows_details = array[1];
    graphic_card = array[2];
    ram_detail = array[3];
    procesor_detail = array[4];
  }
 int main(int argc, char *argv[]){
   char *array[] = { argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]};
   function(array);
 }

所以,当我按如下方式执行程序 exe 时,我得到了正确的输出:

 sample.exe windows_32bit nividia 5Gb i7processor

但我担心的是每次值都必须按特定顺序排列,即用户必须注意“windows_details”将是第一个命令行参数,然后是“graphic_card”,同样是 ram_details 和 processor_details,所以这个解决方案是不稳健,即如果值的序列互换,结果将不正确。我希望解决方案是序列独立的,无论要在正确位置替换的值的序列是什么,以及要作为值传递给选项的命令行参数。例如如下:

sample.exe --configuration i7_processor 5GB windows_32bit nividia or
sample.exe --configuration 5GB i7_processor nividia windows_32bit or
sample.exe --configuration nividia windows_32bit 5GB i7_processor
.
.

所以,像上面一样,有“--configuration”选项,然后是任何顺序的详细信息。我尝试按如下方式添加选项部分,但它不起作用:

int main(int argc, char *argv[]){
   char *array[] = { argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]};
   std::string arg = *(reinterpret_cast<std::string*>(&array));
   if (arg == "--configuration"){
    function(array);
    }
    else {
    std::cout << "Usage " << argv[0] << "\t--config\t\t Specify the configuration of target" << std::endl;
    return 1;
    }
   return 0;       
 }

所以,请帮助我解决我的问题。我应该怎么做才能使它更健壮以及添加选项?

【问题讨论】:

标签: c++ command-line


【解决方案1】:

您应该使用getoptBoost.ProgramOptions*(reinterpret_cast&lt;std::string*&gt;(&amp;array)) 很傻,不会做你认为的事情。

有很多关于如何使用这两种方法的教程。这是来自getopt manual 的示例和指向Boost.ProgramOptions documentation 的链接

一个简单的 BPO 示例如下所示:

    po::options_description desc("Allowed options");
    desc.add_options()
        ("help", "produce help message")
        ("arch", po::value< string >(),
              "Windows version")
        ("card", po::value< string >(),
              "Graphics card")
        ("ram", po::value< string >(),
              "Amount of RAM")
        ("cpu", po::value< string >(),
              "Type of processor")
    ;

    po::variables_map vm;
    po::store(po::command_line_parser(ac, av).
              options(desc).run(), vm);
    po::notify(vm);

    if (vm.count("help")) {
        cout << "Usage: options_description [options]\n";
        cout << desc;
        return 0;
    }

如果您愿意,您可以包括对位置选项的支持,但我并不完全清楚您打算如何在没有显式开关的情况下区分各种选项。

【讨论】:

    【解决方案2】:

    另一种没有第三方库的简单方法是 using specific flags as (-p , -s , -c...) 对应于参数描述或角色。

    然后,在您的代码中,您将拥有如下内容:

    if (i + 1 != argc) // Check that we haven't finished parsing already
        if (argv[i] == "-f") {
            // We know the next argument *should* be the filename:
            myFile = argv[i + 1];
        } else if (argv[i] == "-p") {
            myPath = argv[i + 1];
        } else if (argv[i] == "-o") {
            myOutPath = argv[i + 1];
        } else {
            std::cout << "Not enough or invalid arguments, please try again.\n";
            Sleep(2000);
                     /*
                      *  Sleep for 2 seconds to allow user (if any) to read above statement. 
                      *  The issue with this is that if we're a backend program to a GUI as mentioned above; 
                      *  that program would also sleep for 2 seconds. Most programs don't
                      *  have this - the console will keep the text there until it scrolls off the page or whatever, so you may aswell leave it out.
                      ***/
            exit(0);
        }
    

    【讨论】:

      【解决方案3】:

      Boost.Program_options 应该是实现这一目标的最佳选择, 但是,如果您不想提升并且可以更改函数调用以接受地图,则可以使用 非常易于维护和可读的代码,如下所示:


      void function( std::unordered_map<std::string, std::string>& arrayMap )
      {
          windows_details = arrayMap ["--configuration"];
          graphic_card =    arrayMap ["--graphic_card"];
          ram_detail =      arrayMap ["--ram_detail"];
          procesor_detail = arrayMap ["--procesor_detail"];
      }
      

      使用std::find提取参数的函数

      char* extractOption(char** startItr, char** endItr, 
                           const std::string& searchParam)
      {
          char ** itr = std::find(startItr, endItr, searchParam);
          if (itr != endItr && ++itr != endItr)
          {
              return *itr;
          }
          return 0;
      }
      

      然后您可以使用提取功能来填充地图,如下所示, (仅限伪代码):

      int main(int argc, char * argv[])
      {
          std::unordered_map<std::string, std::string> optionMap;
      
          std::string options[] = { "--configuration", 
                                    "--processor_details" , 
                                    "--graphic_card" };
      
          for(const auto& opts: options)
          {
             char * value= extractOption(argv, argv + argc, opts );
      
             if (value)
             {
                 optionMap[opts] = value ;
             }
             else
             {
                 std::cout << "Not found : " << opts << '\n';
             }
           }
      
          function( optionMap ) ;
          return 0;
      }
      

      See Here

      【讨论】:

      • 我按照你的建议做了,但它给出了编译错误,因为 'i' 没有初始化(int main)。似乎“我”也需要增加。那么,您能否对主要部分的 for 循环有所了解。
      • 在主循环中,我用 opts 替换了 options[i],然后我做了:sample.exe --configuration windows_32 --processor_details i7 --graphic_card nividia 并得到输出:windows_details = 3084545545878, Grphic_card = 89546646 .....所以,输出似乎取的是地址而不是值。我不知道该怎么做才能拥有价值观。
      • @Learner 更新帖子,添加在线演示链接
      【解决方案4】:

      如果您想要一种简单的方式让程序看起来“智能”,那么您会想到正则表达式。

      这是一个简单的开始,您可以根据需要添加更多选项和错误检查。

      #include <iostream>
      #include <regex>
      #include <string>
      #include <vector>
      #include <algorithm>
      #include <cstring>
      
      /*
       sample.exe --configuration i7_processor 5GB windows_32bit nividia
       sample.exe --configuration 5GB i7_processor nividia windows_32bit
       sample.exe --configuration nividia windows_32bit 5GB i7_processor
       */
      
      template<class Iter>
      std::pair<Iter, Iter> find_config(Iter first, Iter last)
      {
          auto begin = std::find(first, last, std::string("--configuration"));
          if (begin == last)
          {
              return { last, last };
          }
          begin = std::next(begin);
          auto end = std::find_if(begin, last, [](auto& opt) {
              return opt.substr(0, 2) == "--";
          });
          return { begin, end };
      }
      
      struct configuration
      {
          std::string processor = "not set";
          unsigned long long memory = 0;
          std::string os = "not set";
          std::string graphics = "not set";
      };
      
      std::ostream& operator<<(std::ostream& os, const configuration& conf)
      {
          os <<   "processor = " << conf.processor;
          os << "\nmemory    = " << conf.memory;
          os << "\nos        = " << conf.os;
          os << "\ngraphics  = " << conf.graphics;
          return os;
      }
      
      const std::regex re_processor("(.*)_processor", std::regex::icase);
      const std::regex re_memory("(\\d+)(mb|gb)", std::regex::icase);
      
      template<class Iter>
      auto parse_config(Iter first, Iter last) -> configuration
      {
          configuration result;
      
          for ( ; first != last ; ++first)
          {
              auto& option = *first;
              std::smatch match;
              if (std::regex_match(option, match, re_processor))
              {
                  result.processor = match[1].str();
                  continue;
              }
      
              if (std::regex_match(option, match, re_memory))
              {
                  unsigned long long num = std::stoi(match[1].str());
                  auto mult = match[2].str();
                  std::transform(mult.begin(), mult.end(), 
                                 mult.begin(), 
                                 [](auto& c) { return std::toupper(c); });
                  if (mult == "GB") {
                      num *= 1024 * 1024 * 1024;
                  }
                  else {
                      num *= 1024 * 1024;
                  }
                  result.memory = num;
                  continue;
              }
      
          }
      
          return result;
      }
      
      int main(int argc, const char* const * argv)
      {
          std::vector<std::string> args(argv, argv + argc);
          const auto& progname = &args[0];
      
          auto config_range = find_config(std::next(std::begin(args)), std::end(args));
          auto configuration = parse_config(config_range.first, config_range.second);
      
          std::cout << configuration << std::endl;
      
      }
      

      示例运行:

      $ sample --configuration i7_processor 5GB windows_32bit nividia
      processor = i7
      memory    = 5368709120
      os        = not set
      graphics  = not set
      $ 
      

      【讨论】:

        猜你喜欢
        • 2013-12-23
        • 2013-12-12
        • 2012-08-13
        • 2017-03-01
        • 2015-04-17
        • 2014-07-22
        • 2010-10-30
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多