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

prog [-abc] [input [output]]

是否有某种方法可以在标准库中实现这一点,或者我需要自己编写代码?


相关:

在unicode c++应用程序中解析命令行参数


当前回答

它太大了,不可能包含在Stack Overflow回答中,但我创建了一个用于声明式定义命令行的库。它利用了c++ 14通过给每个成员变量赋初始值来构建类构造函数的能力。

这个库基本上是一个基类。要定义命令语法,需要声明一个派生自该语法的结构。下面是一个例子:

struct MyCommandLine : public core::CommandLine {
    Argument<std::string> m_verb{this, "program", "program.exe",
        "this is what my program does"};
    Option<bool> m_help{this, "help", false,
        "displays information about the command line"};
    Alias<bool> alias_help{this, '?', &m_help};
    Option<bool> m_demo{this, "demo", false,
        "runs my program in demonstration mode"};
    Option<bool> m_maximize{this, "maximize", false,
        "opens the main window maximized"};
    Option<int> m_loops{this, "loops", 1,
        "specifies the number of times to repeat"};
    EnumOption<int> m_size{this, "size", 3,
                           { {"s", 1},
                             {"small", 1},
                             {"m", 3},
                             {"med", 3},
                             {"medium", 3},
                             {"l", 5},
                             {"large", 5} } };
    BeginOptionalArguments here{this};
    Argument<std::string> m_file{this, "file-name", "",
        "name of an existing file to open"};
} cl;

参数、选项和别名类模板是在CommandLine基类的范围内声明的,您可以为自己的类型专门化它们。每个选项都包含this指针、选项名称、默认值和用于打印命令概要/用法的描述。

我仍然在寻找消除所有这些指针的需要,但我还没有找到一种不引入宏的方法。这些指针允许每个成员向驱动解析的基类中的表注册自己。

一旦有了实例,就会有多个方法重载来解析来自字符串或主样式参数向量的输入。解析器同时处理windows风格和unix风格的选项语法。

if (!cl.Parse(argc, argv)) {
    std::string message;
    for (const auto &error : cl.GetErrors()) {
        message += error + "\n";
    }
    std::cerr << message;
    exit(EXIT_FAILURE);
}

一旦它被解析,你可以使用operator()访问任何选项的值:

if (cl.m_help()) { std::cout << cl.GetUsage(); }
for (int i = 0; i < cl.m_loops(); ++i) { ... }

整个库只有大约300行(不包括测试)。实例有点臃肿,因为解析表是实例(而不是类)的一部分。但是每个程序通常只需要一个实例,而且这种纯声明性方法的便利性非常强大,可以通过解析新输入简单地重置实例。

其他回答

如果你只想自己处理命令行选项,最简单的方法是:

vector<string> args(argv + 1, argv + argc);

在main()的顶部。这将把所有命令行参数复制到std::strings的向量中。然后,您可以使用==轻松地比较字符串,而不是无休止的strcmp()调用。例如:

int main(int argc, char **argv) {
    vector<string> args(argv + 1, argv + argc);
    string infname, outfname;

    // Loop over command-line args
    // (Actually I usually use an ordinary integer loop variable and compare
    // args[i] instead of *i -- don't tell anyone! ;)
    for (auto i = args.begin(); i != args.end(); ++i) {
        if (*i == "-h" || *i == "--help") {
            cout << "Syntax: foomatic -i <infile> -o <outfile>" << endl;
            return 0;
        } else if (*i == "-i") {
            infname = *++i;
        } else if (*i == "-o") {
            outfname = *++i;
        }
    }
}

[编辑:我意识到我正在复制argv[0],程序的名称,到args -已修复。]

提振。Program_options应该可以做到这一点

谷歌的旗帜

我可以建议使用模板化的c++命令行解析器库(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; }
}

我认为GNU GetOpt并不是马上就可以使用的。

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

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

例如,调用:

 ./myProgram -v -p 1234

地图将是:

 ["-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;
}