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

211

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

prog [-abc] [input [output]]

标准库中是否有实现这个功能的方法,还是我需要编写自己的代码?


相关问题:


我认为Nunit源代码(C#)有一个很好的命令行处理类的示例... - Mitch Wheat
1
最简单的方法是使用其中一个参数解析库:getoptargparse - Andrejs Cainikovs
如果您无法使用库(例如boost),请至少使用std :: vector <std :: string> args(argv,argv + argc);,以便您可以解析字符串向量而不是字符数组。 - stefaanv
1
对于已经在他们的程序中使用OpenCV的人来说,cv::CommandLineParser也可能是一个不错的选择。[我的意思是以防你已经为其他目的在使用它,我并不是要将OpenCV包含进命令行解析器中。] - Dharma
1
最近使用现代C++为此编写了代码:https://github.com/juzzlin/Argengine - juzzlin
请查看 https://github.com/p-ranav/argparse 进行检出。 - droptop
42个回答

18

我发现使用ezOptionParser更容易。它是一个单个头文件,除了STL之外不依赖任何东西,在Windows和Linux上工作(很可能也适用于其他平台),由于示例的存在没有学习曲线,具有其他库所没有的功能(例如带注释的文件导入/导出、带分隔符的任意选项名称、自动使用格式等),并且具有LGPL许可证。


1
从版本0.1.3开始,许可证现在是MIT。我正在尝试在新项目中使用它,而不是TCLAP,到目前为止看起来非常有前途。文件配置选项非常好。 - Sean
6
我刚试用了 exOptionParser,但它存在很多问题。首先,我收到了58个有关无符号整数转换为整数的警告。它还尝试增加列表迭代器(这样使用是错误的),导致程序崩溃。它的用户界面也很糟糕。它到处都使用引用来代替返回所需的数据。尽管它是建立在C++ STL之上,但看起来像一个C库。 - Andrew Larsson
注意:无法检测未知参数。此外,如果头文件不放在其他头文件之前,则会出现编译错误。我会寻找另一个解析器... - Totte Karlsson

18

你可能需要使用外部库来实现此功能。有许多可供选择。

Boost拥有一个非常丰富的库Boost Program Options(像往常一样)。

我个人在过去几年中最喜欢的是TCLAP——纯模板化,因此没有库或链接,自动生成“--help”等好处。请参见文档中的最简单的示例


+1,之前不知道tclap,它看起来既轻量级又感觉完整,我一定会深入研究。 - Matthieu M.

16

同时也有一份Google开源库可供使用。

实际上,命令行解析已经“解决”了。只需要选择一个即可。


感谢@QPaysTaxes注意到链接已经失效;我不知道为什么你的编辑被拒绝了,但你肯定是正确的。 - Max Lybbert
5
我无法想象有比这个回答更没有帮助的了。“问题解决了,选一个吧。”抱歉,“当然啦”。我大约思考了15分钟关于如何解决这个问题,想到了大约30种不同的情境。我猜想“正确”的回复更像是解释特定的问题会导致一系列代码实现的情形。但是,嘿,感谢你的来电。 - user1357713

15

11

它是否允许您显示当前和默认值?几年前,我不得不实现一个包装器来显示有效选项(基于解析的配置/参数,而不仅仅是显示提供的字符串)。 - Jonathan Graehl

9

我认为GNU GetOpt不太容易使用。

Qt和Boost可能是解决方案,但需要下载和编译大量代码。

因此,我自己实现了一个解析器,它可以生成一个参数的std::map<std::string,std::string>。

例如,调用:

 ./myProgram -v -p 1234

map将是:

 ["-v"][""]
 ["-p"]["1234"]

使用方法如下:

int main(int argc, char *argv[]) {
    MainOptions mo(argc, argv);
    MainOptions::Option* opt = mo.getParamFromKey("-p");
    const string type = opt ? (*opt).second : "";
    cout << type << endl; /* Prints 1234 */
    /* Your check code */
}

MainOptions.h

#ifndef MAINOPTIONS_H_
#define MAINOPTIONS_H_

#include <map>
#include <string>

class MainOptions {
public:
    typedef std::pair<std::string, std::string> Option;
    MainOptions(int argc, char *argv[]);
    virtual ~MainOptions();
    std::string getAppName() const;
    bool hasKey(const std::string&) const;
    Option* getParamFromKey(const std::string&) const;
    void printOptions() const;
private:
    typedef std::map<std::string, std::string> Options;
    void parse();
    const char* const *begin() const;
    const char* const *end() const;
    const char* const *last() const;
    Options options_;
    int argc_;
    char** argv_;
    std::string appName_;
};

MainOptions.cpp

#include "MainOptions.h"

#include <iostream>

using namespace std;

MainOptions::MainOptions(int argc, char* argv[]) :
        argc_(argc),
        argv_(argv) {
    appName_ = argv_[0];
    this->parse();
}

MainOptions::~MainOptions() {
}

std::string MainOptions::getAppName() const {
    return appName_;
}

void MainOptions::parse() {
    typedef pair<string, string> Option;
    Option* option = new pair<string, string>();
    for (const char* const * i = this->begin() + 1; i != this->end(); i++) {
        const string p = *i;
        if (option->first == "" && p[0] == '-') {
            option->first = p;
            if (i == this->last()) {
                options_.insert(Option(option->first, option->second));
            }
            continue;
        } else if (option->first != "" && p[0] == '-') {
            option->second = "null"; /* or leave empty? */
            options_.insert(Option(option->first, option->second));
            option->first = p;
            option->second = "";
            if (i == this->last()) {
                options_.insert(Option(option->first, option->second));
            }
            continue;
        } else if (option->first != "") {
            option->second = p;
            options_.insert(Option(option->first, option->second));
            option->first = "";
            option->second = "";
            continue;
        }
    }
}

void MainOptions::printOptions() const {
    std::map<std::string, std::string>::const_iterator m = options_.begin();
    int i = 0;
    if (options_.empty()) {
        cout << "No parameters\n";
    }
    for (; m != options_.end(); m++, ++i) {
        cout << "Parameter [" << i << "] [" << (*m).first << " " << (*m).second
                << "]\n";
    }
}

const char* const *MainOptions::begin() const {
    return argv_;
}

const char* const *MainOptions::end() const {
    return argv_ + argc_;
}

const char* const *MainOptions::last() const {
    return argv_ + argc_ - 1;
}

bool MainOptions::hasKey(const std::string& key) const {
    return options_.find(key) != options_.end();
}

MainOptions::Option* MainOptions::getParamFromKey(
        const std::string& key) const {
    const Options::const_iterator i = options_.find(key);
    MainOptions::Option* o = 0;
    if (i != options_.end()) {
        o = new MainOptions::Option((*i).first, (*i).second);
    }
    return o;
}

2
“… is not too immediate to use” 是什么意思?你能详细说明一下吗? - Peter Mortensen

8

GNU C库中有这些工具,其中包括GetOpt

如果您正在使用Qt并且喜欢GetOpt界面,则froglogic在此处发布了一个不错的接口here


7

如果我可以自吹自擂的话,我想建议您看一下我编写的一个选项解析库:dropt

  • 它是一个C库(如果需要,还有一个C++包装器)。
  • 它很轻量级。
  • 它是可扩展的(可以很容易地添加自定义参数类型,并且与内置参数类型平起平坐)。
  • 它应该非常易于移植(它是用标准C语言编写的),没有任何依赖项(除了C标准库)。
  • 它具有非常宽松的许可证(zlib/libpng)。

它提供的一个许多其他库都没有的特性是能够覆盖早期的选项。例如,如果您有一个shell别名:

alias bar="foo --flag1 --flag2 --flag3"

如果你想使用bar,但需要禁用--flag1,可以这样做:

bar --flag1=0

1
这看起来相当不错。很高兴我向下滚动了一下;对于纯C语言来说,除了这个,没有什么好的东西! - Asherah
听起来不错,但似乎有点太大了。另外,我只是在寻找一种从参数中解析出scanf格式说明符的方法,我已经编写了自己的(虽然更基本)解析器。 - MarcusJ
1
@MarcusJ,你说它太大了有点奇怪(它比大多数其他命令行选项解析器要小得多),但是你希望它解析printf/scanf格式说明符(这不是命令行选项解析器通常做的事情)... - jamesdlin
是的,我知道我对此有一些特定要求,我将继续重写我的选项解析器,但我确实查看了您的代码,并且传递结构以包含各种选项的想法非常有趣(到目前为止,我的选项解析器只是硬编码的)。它本身并不太大,只是我有一个单独的.c/.h项目,而您的代码将使我已经拥有的代码量增加一倍,因此对于我的特定项目来说太大了。 - MarcusJ

6

我喜欢C语言的getopt()函数,但我已经老了。 :-)



6

看起来适合简单使用。我一直喜欢“选项作为全局变量定义在任何地方”的小工具。只有基本类型和字符串,这可能是一个很大的缺点。 - Jonathan Graehl

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接