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

时间:2009-05-14 20:46:45

标签: c++ command-line arguments

  

可能重复:
  What parameter parser libraries are there for C++?

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

prog [-abc] [input [output]]

STL中是否有库来执行此操作?


相关:

11 个答案:

答案 0 :(得分:221)

boost::program_options和GNU 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;
}

在使用这种方法注意的事项上,必须使用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;
}

答案 1 :(得分:81)

Boost.Program_options应该做的伎俩

答案 2 :(得分:57)

我可以建议Templatized C++ Command Line Parser Library(某些forks on GitHub可用),API非常简单并且(从网站引用):

  

库完全在头文件中实现,使其变得容易   使用和分发其他软件。它是在麻省理工学院获得许可的   无忧分发许可证。

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

#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; }
}

答案 3 :(得分:32)

您可以使用GNU GetOpt(LGPL)或其中一个C ++端口,例如getoptpp(GPL)。

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

// 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;
}

答案 4 :(得分:21)

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

http://optionparser.sourceforge.net

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

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

  

if(options [HELP])...;

  

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

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

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

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

答案 5 :(得分:10)

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);
    }

}

答案 6 :(得分:5)

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

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

答案 7 :(得分:3)

如果你可以使用Boost lib,我建议使用boost :: program_options。

STL和常规C ++ / C运行时库中没有任何特定内容。

答案 8 :(得分:2)

尝试CLPP库。它是用于命令行参数解析的简单而灵活的库。仅限标题和跨平台。仅使用ISO C ++和Boost C ++库。恕我直言,它比Boost.Program_options更容易。

图书馆:http://sourceforge.net/projects/clp-parser

2010年10月26日 - 新版本2.0rc。修复了许多错误,修改了源代码,文档,示例和注释的完全重构。

答案 9 :(得分:2)

很晚才回答,但我在一些项目中使用了GetPot: http://getpot.sourceforge.net/

主要功能:一切都在一个头文件中,没有构建麻烦。只需将其保存在计算机上的某个位置,然后将“#include”保存在包含main()

的文件中

最近没有更新,但很好地记录,并且运作良好。你可以尝试一下。

答案 10 :(得分:-1)

一个简单的解决方案是将 argv 放入 std::map 以便于查找:

map<string, string> argvToMap(int argc, char * argv[])
{
    map<string, string> args;

    for(int i=1; i<argc; i++) {
        if (argv[i][0] == '-') {
            const string key = argv[i];
            string value = "";
            if (i+1 < argc && argv[i+1][0] != '-') {
                value = string(argv[i+1]);
                i++;
            }

            args[key] = value;
        }
    }

    return args;
}

示例用法:

#include <map>
#include <string>
#include <iostream>

using namespace std;

map<string, string> argvToMap(int argc, char * argv[])
{
    map<string, string> args;

    for(int i=1; i<argc; i++) {
        if (argv[i][0] == '-') {
            const string key = argv[i];
            string value = "";
            if (i+1 < argc && argv[i+1][0] != '-') {
                value = string(argv[i+1]);
                i++;
            }

            args[key] = value;
        }
    }

    return args;
}

void printUsage()
{
    cout << "simple_args: A sample program for simple arg parsing\n"
            "\n"
            "Example usage:\n"
            "    ./simple_args --print-all --option 1 --flag 2\n";
}

int main(int argc, char * argv[])
{
    auto args = argvToMap(argc, argv);

    if (args.count("-h") || args.count("--help")) {
        printUsage();
    }
    else if (args.count("--print-all")) {
        for (auto const & pair: args)
            cout << "{" << pair.first << ": " << pair.second << "}\n";
    }

    return 0;
}

输出:

$ ./simple_args --print-all --option 1 --flag "hello world"
{--flag: hello world}
{--option: 1}
{--print-all: }

这种方法肯定有很大的局限性,但我发现它在简单性和实用性之间取得了很好的平衡。