如何在C++中解析命令行参数?

Ver*_*gen 183 c++ command-line arguments

可能重复:
C++有哪些参数解析器库?

如果指定程序运行如下,那么在C++中解析命令行参数的最佳方法是什么:

prog [-abc] [input [output]]
Run Code Online (Sandbox Code Playgroud)

STL中有一个库可以做到这一点吗?


有关:

iai*_*ain 233

boost::program_optionsGNU getopt 的建议很好.

但是,对于简单的命令行选项,我倾向于使用std :: find

例如,在-f命令行参数后读取文件的名称.您还可以检测是否已传入单字选项,以-h获取帮助.

#include <algorithm>

char* getCmdOption(char ** begin, char ** end, const std::string & option)
{
    char ** itr = std::find(begin, end, option);
    if (itr != end && ++itr != end)
    {
        return *itr;
    }
    return 0;
}

bool cmdOptionExists(char** begin, char** end, const std::string& option)
{
    return std::find(begin, end, option) != end;
}

int main(int argc, char * argv[])
{
    if(cmdOptionExists(argv, argv+argc, "-h"))
    {
        // Do stuff
    }

    char * filename = getCmdOption(argv, argv + argc, "-f");

    if (filename)
    {
        // Do interesting things
        // ...
    }

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

在使用这种方法要注意的事项上,必须使用std :: strings作为std :: find的值,否则将对指针值执行相等性检查.


我希望可以编辑此响应而不是添加新响应,因为这是基于原始答案.我稍微重写了函数并将它们封装在一个类中,所以这里是代码.我认为以这种方式使用也是可行的:

class InputParser{
    public:
        InputParser (int &argc, char **argv){
            for (int i=1; i < argc; ++i)
                this->tokens.push_back(std::string(argv[i]));
        }
        /// @author iain
        const std::string& getCmdOption(const std::string &option) const{
            std::vector<std::string>::const_iterator itr;
            itr =  std::find(this->tokens.begin(), this->tokens.end(), option);
            if (itr != this->tokens.end() && ++itr != this->tokens.end()){
                return *itr;
            }
            static const std::string empty_string("");
            return empty_string;
        }
        /// @author iain
        bool cmdOptionExists(const std::string &option) const{
            return std::find(this->tokens.begin(), this->tokens.end(), option)
                   != this->tokens.end();
        }
    private:
        std::vector <std::string> tokens;
};

int main(int argc, char **argv){
    InputParser input(argc, argv);
    if(input.cmdOptionExists("-h")){
        // Do stuff
    }
    const std::string &filename = input.getCmdOption("-f");
    if (!filename.empty()){
        // Do interesting things ...
    }
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

  • 当然,如果你想要posix样式命令行选项,那么你应该使用其他答案中提到的命令行处理库之一.正如这个答案所说,这是针对简单的命令行选项. (9认同)
  • 这没有按预期工作,例如,像tar应用程序:`tar -xf file`,对吗?每个选项必须分开.`grep -ln pattern file`不会被理解,但必须是`grep -l -n pattern file`. (7认同)
  • 这开箱即用.但请注意,option参数是`const std :: string&`.重要的是`std :: find`的value参数是`std :: string`,因此使用`std :: string :: operator ==()`而不是`char*operator ==()` (因为后者只会比较指针值而不是字符串内容). (4认同)
  • 这很好,但需要两个小的改进:首先,构造函数params应该是const限定的,其次,getCmdOption的返回值应该是一个值,而不是引用,否则你会遇到http://stackoverflow.com/questions/ 1339601 /预警 - 返回引用到暂时的.除此之外,一个很好的简单解决方案,我会用它,谢谢. (3认同)
  • 1. @iain这段代码的许可证是什么?2.当'getCmdOption`返回空字符串时,@TomášDvořák对临时的返回引用是正确的.`InputParser`应该有一个`std :: string empty_string`作为成员,并在找不到该选项时返回它的引用. (2认同)
  • 第一部分是我的,第二部分是其他人的编辑。关于我的代码,没有许可证,它是公共领域的,可以随意使用,没有任何保证。我不确定第二个。关于InputParser类,我很高兴它为我的回答做出了贡献,但是我不会将args放入向量中,而是将argv和argv + argc存储为开始和结束迭代器。 (2认同)

小智 84

Boost.Program_options 应该可以解决问题

  • 为了解析命令行选项而引入代码库的提升是一个"破解坚果的大锤".如果增强已经使用它.否则看看像gopt这样的东西.没有什么比一般的提升,但它有点重量级,我发现版本与g ++版本紧密相关. (66认同)
  • 此外,boost :: program_options不是仅头文件库.你必须建立提升.这很麻烦. (23认同)
  • 好的选择.或者,如果由于某种原因你不能使用boost,那么基于标准c的"getopt"功能也可以完成工作. (12认同)
  • boost :: program_options的文档可能更完整.特别难以找到如何使用文件来保留选项,这是一个关键功能. (11认同)
  • 对于这项任务而言,提升似乎完全有点过头了 (7认同)

nau*_*ghi 58

我可以建议Templatized C++ Command Line Parser Library(GitHub上的一些分支可用),API非常简单并且(从网站引用):

该库完全在头文件中实现,使其易于使用和与其他软件一起分发.它根据MIT许可证进行许可,以实现无忧分发.

这是手册中的一个示例,为简单起见,此处着色:

#include <string>
#include <iostream>
#include <algorithm>
#include <tclap/CmdLine.h>

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

    // Wrap everything in a try block.  Do this every time,
    // because exceptions will be thrown for problems.
    try {

    // Define the command line object, and insert a message
    // that describes the program. The "Command description message"
    // is printed last in the help text. The second argument is the
    // delimiter (usually space) and the last one is the version number.
    // The CmdLine object parses the argv array based on the Arg objects
    // that it contains.
    TCLAP::CmdLine cmd("Command description message", ' ', "0.9");

    // Define a value argument and add it to the command line.
    // A value arg defines a flag and a type of value that it expects,
    // such as "-n Bishop".
    TCLAP::ValueArg<std::string> nameArg("n","name","Name to print",true,"homer","string");

    // Add the argument nameArg to the CmdLine object. The CmdLine object
    // uses this Arg to parse the command line.
    cmd.add( nameArg );

    // Define a switch and add it to the command line.
    // A switch arg is a boolean argument and only defines a flag that
    // indicates true or false.  In this example the SwitchArg adds itself
    // to the CmdLine object as part of the constructor.  This eliminates
    // the need to call the cmd.add() method.  All args have support in
    // their constructors to add themselves directly to the CmdLine object.
    // It doesn't matter which idiom you choose, they accomplish the same thing.
    TCLAP::SwitchArg reverseSwitch("r","reverse","Print name backwards", cmd, false);

    // Parse the argv array.
    cmd.parse( argc, argv );

    // Get the value parsed by each arg.
    std::string name = nameArg.getValue();
    bool reverseName = reverseSwitch.getValue();

    // Do what you intend.
    if ( reverseName )
    {
            std::reverse(name.begin(),name.end());
            std::cout << "My name (spelled backwards) is: " << name << std::endl;
    }
    else
            std::cout << "My name is: " << name << std::endl;


    } catch (TCLAP::ArgException &e)  // catch any exceptions
    { std::cerr << "error: " << e.error() << " for arg " << e.argId() << std::endl; }
}
Run Code Online (Sandbox Code Playgroud)

  • 这个选项对我来说是最简单的,虽然它确实为我的程序添加了一个带有多个头文件的子目录.包含路径需要相应地进行编辑. (3认同)

Mat*_*hen 33

您可以使用GNU GetOpt(LGPL)或各种C++端口之一,例如getoptpp(GPL).

使用GetOpt的简单示例(prog [-ab]输入)如下:

// C Libraries:
#include <string>
#include <iostream>
#include <unistd.h>

// Namespaces:
using namespace std;

int main(int argc, char** argv) {
    int opt;
    string input = "";
    bool flagA = false;
    bool flagB = false;

    // Retrieve the (non-option) argument:
    if ( (argc <= 1) || (argv[argc-1] == NULL) || (argv[argc-1][0] == '-') ) {  // there is NO input...
        cerr << "No argument provided!" << endl;
        //return 1;
    }
    else {  // there is an input...
        input = argv[argc-1];
    }

    // Debug:
    cout << "input = " << input << endl;

    // Shut GetOpt error messages down (return '?'): 
    opterr = 0;

    // Retrieve the options:
    while ( (opt = getopt(argc, argv, "ab")) != -1 ) {  // for each option...
        switch ( opt ) {
            case 'a':
                    flagA = true;
                break;
            case 'b':
                    flagB = true;
                break;
            case '?':  // unknown option...
                    cerr << "Unknown option: '" << char(optopt) << "'!" << endl;
                break;
        }
    }

    // Debug:
    cout << "flagA = " << flagA << endl;
    cout << "flagB = " << flagB << endl;

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

  • 仅供参考,GNU getopt是GPL,而getoptpp也是GPL,因此对于非开源软件,boost变量可能更好. (5认同)
  • @SorinSbarnea,你看看我的链接了吗?我应该更清楚,但我指的是getopt和getopt-gnu,而不是getoptpp. (2认同)

小智 22

另一个替代方案是精益平均C++选项解析器:

http://optionparser.sourceforge.net

它是一个仅限标头的库(事实上只是一个头文件),与其他所有建议不同,它也是独立的,即它没有任何依赖性.特别是对STL没有依赖性.它甚至不使用需要库支持的异常或其他任何东西.这意味着它可以与普通的C或其他语言链接而不引入"外国"库.

像boost :: program_options一样,它的API提供了方便的直接访问选项,即你可以编写这样的代码

if(选项[HELP])...;

int verbosity = options [VERBOSE] .count();

与boost :: program_options不同,这只是使用一个用(用户提供的)枚举索引的数组.这提供了没有重量的关联容器的便利性.

它有详细记录,并拥有公司友好许可证(MIT).

TLMC++ OP包含一个很好的格式化用户消息,可以进行换行和列对齐,这对于本地化程序非常有用,因为它确保即使在具有较长消息的语言中输出也会很好.它还可以节省您手动格式化80列使用情况的麻烦.


小智 14

for (int i = 1; i < argc; i++) {

    if (strcmp(argv[i],"-i")==0) {
        filename = argv[i+1];
        printf("filename: %s",filename);
    } else if (strcmp(argv[i],"-c")==0) {
        convergence = atoi(argv[i + 1]);
        printf("\nconvergence: %d",convergence);
    } else if (strcmp(argv[i],"-a")==0) {
        accuracy = atoi(argv[i + 1]);
        printf("\naccuracy:%d",accuracy);
    } else if (strcmp(argv[i],"-t")==0) {
        targetBitRate = atof(argv[i + 1]);
        printf("\ntargetBitRate:%f",targetBitRate);
    } else if (strcmp(argv[i],"-f")==0) {
        frameRate = atoi(argv[i + 1]);
        printf("\nframeRate:%d",frameRate);
    }

}
Run Code Online (Sandbox Code Playgroud)

  • 我发现评论非常苛刻.我认为在不使用库的情况下展示如何实现这一点的例子也是公平的.这个答案是补充+1,对不起. (22认同)
  • @RobertMunafo:对`argv [i + 1]`的引用很容易超出`argv`数组的范围.考虑使用`" - i"`作为*last*参数运行程序. (7认同)
  • -1:这将获取没有绑定检查的数组元素 (3认同)
  • -1:因为没有边界检查 (2认同)
  • -1 作为“自己动手”的答案,而该问题专门询问将完成任务的“STL 库”。此外,正如 Keith Thompson 和 mmutz 所指出的,边界检查中存在一些错误。 (2认同)

Jay*_*Jay 5

AnyOption是一个C++类,可以轻松解析复杂的命令行选项.它还以选项值对格式解析rsourcefile中的选项.

AnyOption实现了传统的POSIX样式字符选项(-n)以及较新的GNU样式长选项(--name).或者,您可以通过要求忽略POSIX样式选项来使用更简单的长选项版本(-name).


max*_*zig 5

Qt 5.2 带有一个命令行解析器 API

小例子:

#include <QCoreApplication>
#include <QCommandLineParser>
#include <QDebug>

int main(int argc, char **argv)
{
  QCoreApplication app(argc, argv);
  app.setApplicationName("ToolX");
  app.setApplicationVersion("1.2");

  QCommandLineParser parser;
  parser.setApplicationDescription("Tool for doing X.");
  parser.addHelpOption();
  parser.addVersionOption();
  parser.addPositionalArgument("infile",
      QCoreApplication::translate("main", "Input file."));

  QCommandLineOption verbose_opt("+",
      QCoreApplication::translate("main", "be verbose"));
  parser.addOption(verbose_opt);

  QCommandLineOption out_opt(QStringList() << "o" << "output",
      QCoreApplication::translate("main", "Output file."),
      QCoreApplication::translate("main", "filename"), // value name
      QCoreApplication::translate("main", "out")   // default value
      );
  parser.addOption(out_opt);

  // exits on error
  parser.process(app);

  const QStringList args = parser.positionalArguments();

  qDebug() << "Input files: " << args
    << ", verbose: " << parser.isSet(verbose_opt)
    << ", output: " << parser.value(out_opt)
    << '\n';
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

示例输出

自动生成的帮助屏幕:

$ ./qtopt -h
用法:./qtopt [options] infile
做 X 的工具。

选项:
  -h, --help 显示此帮助。
  -v, --version 显示版本信息。
  -+ 冗长
  -o, --output 输出文件。

参数:
  infile 输入文件。

自动生成的版本输出:

$ ./qtopt -v
工具X 1.2

一些真实的电话:

$ ./qtopt b1 -+ -o tmp blah.foo
输入文件: ("b1", "blah.foo") ,详细: true ,输出: "tmp"
$ ./qtopt          
输入文件: () ,详细: false ,输出: "out"

解析错误:

$ ./qtopt --hlp
未知选项 'hlp'。
$回声 $?
1

结论

如果您的程序已经使用 Qt (>= 5.2) 库,那么它的命令行解析 API 足够方便,可以完成工作。

请注意,内置 Qt 选项QApplication在选项解析器运行之前被消耗。