211

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

prog [-abc] [input [output]]

有什么方法可以在标准库中内置,还是我需要编写自己的代码?


有关的:

4

42 回答 42

271

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;
}
于 2009-05-15T14:09:54.090 回答
89

Boost.Program_options 应该可以解决问题

于 2009-05-14T20:49:37.383 回答
63

我可以建议模板化 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; }
}
于 2011-10-10T11:54:05.793 回答
45

Boost.Program_options

于 2008-10-31T14:02:56.093 回答
37

您可以使用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;
}
于 2009-05-14T20:49:55.073 回答
27

GNU GetOpt

使用 GetOpt 的简单示例:

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

// Namespaces:
using namespace std;

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

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

如果您有接受参数的选项,您也可以使用optarg 。

于 2008-10-31T14:12:30.270 回答
23

另一种选择是精益平均 C++ 选项解析器:

http://optionparser.sourceforge.net

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

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

如果(选项[帮助])...;

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

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

它有据可查,并拥有对公司友好的许可证 (MIT)。

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

于 2012-01-24T18:12:37.777 回答
21
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);
    }

}
于 2011-08-04T23:24:53.920 回答
19

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

于 2011-05-11T23:10:40.403 回答
19

TCLAP是一个非常好的轻量级设计并且易于使用: http ://tclap.sourceforge.net/

于 2012-08-22T22:37:46.947 回答
18

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

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-- 已修复。]

于 2009-01-14T06:50:32.050 回答
18

您可能想为此使用外部库。有很多可供选择。

Boost 有一个功能非常丰富(和往常一样)的库Boost Program Options

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

于 2010-09-30T00:35:34.357 回答
16

并且有一个可用的谷歌图书馆

真的,命令行解析是“解决的”。随便挑一个。

于 2008-10-31T16:46:05.717 回答
15

使用 C++,答案通常是在 Boost...

Boost.Program 选项

于 2009-01-14T01:17:28.977 回答
11

试试Boost::Program Options。它允许您读取和解析命令行以及配置文件。

于 2009-09-17T03:18:46.133 回答
10

我认为 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 */
}

主选项.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;
}
于 2014-07-14T12:44:55.233 回答
8

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

如果您使用 Qt 并且喜欢 GetOpt 界面,froglogic在这里发布了一个不错的界面。

于 2008-10-31T14:19:07.497 回答
7

如果可以的话,我也想建议看一下我编写的选项解析库:dropt

  • 它是一个 C 库(如果需要,可以使用 C++ 包装器)。
  • 它很轻。
  • 它是可扩展的(可以轻松添加自定义参数类型,并且与内置参数类型具有同等地位)。
  • 它应该是非常可移植的(它是用标准 C 编写的),没有依赖项(C 标准库除外)。
  • 它有一个非常不受限制的许可证(zlib/libpng)。

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

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

并且您想使用bar--flag1已禁用,它允许您执行以下操作:

bar --flag1=0
于 2012-10-03T06:11:24.797 回答
6

我喜欢 C 的 getopt(),但是我老了。:-)

于 2009-09-17T03:19:02.277 回答
6

谷歌的gflags

于 2009-09-17T03:20:12.230 回答
5

我建议使用图书馆。有经典而古老的getopt,我相信还有其他人。

于 2009-01-14T01:18:22.970 回答
5

有许多不错的库可用。

Boost Program Options是一个相当重量级的解决方案,因为将它添加到您的项目需要您构建 boost,并且语法有些混乱(在我看来)。但是,它几乎可以做任何事情,包括让命令行选项覆盖配置文件中设置的选项。

SimpleOpt是一个相当全面但简单的命令行处理器。它是一个单独的文件,结构简单,但仅将命令行解析为选项,您必须进行所有类型和范围检查。它适用于 Windows 和 Unix,并且还附带了一个适用于 Windows 的 glob 版本。

getopt 在 Windows 上可用。它与 Unix 机器上的相同,但它通常是一个 GPL 库。

于 2009-04-21T08:45:02.350 回答
5

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

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

于 2011-08-17T15:23:19.390 回答
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;
}

示例输出

自动生成的帮助屏幕:

$ ./qtopt -h
用法:./qtopt [选项] 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"
$ ./qtop          
输入文件: () ,详细: false ,输出:“out”

解析错误:

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

结论

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

请注意,QApplication在选项解析器运行之前会使用内置 Qt 选项。

于 2014-01-12T10:56:00.413 回答
4

命令基本上是一个字符串。一般来说,它可以分为两部分——命令的name和命令的arguments

例子:

ls

用于列出目录的内容:

user@computer:~$ ls
Documents Pictures Videos ...

ls以上是在用户的文件夹内执行的home。此处,要列出的文件夹的参数隐式添加到命令中。我们可以显式传递一些参数:

user@computer:~$ ls Picture
image1.jpg image2.jpg ...

在这里,我已明确告诉ls我想查看哪个文件夹的内容。例如,我们可以使用另一个参数来l列出每个文件和文件夹的详细信息,例如访问权限、大小等:

user@computer:~$ ls Pictures
-rw-r--r-- 1 user user   215867 Oct 12  2014 image1.jpg
-rw-r--r-- 1 user user   268800 Jul 31  2014 image2.jpg
...

哦,大小看起来很奇怪(215867268800)。h让我们为人类友好的输出添加标志:

user@computer:~$ ls -l -h Pictures
-rw-r--r-- 1 user user  211K Oct 12  2014 image1.jpg
-rw-r--r-- 1 user user  263K Jul 31  2014 image2.jpg
...

一些命令允许组合它们的参数(在上述情况下,我们不妨编写ls -lh,我们将得到相同的输出),使用短名称(通常是单个字母,但有时更多;缩写)或长名称(如果ls我们有-a--all用于列出所有文件,包括隐藏文件,其中包含)等--all的长名称-a。有些命令的参数顺序非常重要,但也有一些命令的参数顺序根本不重要

例如,如果我使用ls -lh或者ls -hlmv(移动/重命名文件)的情况下,您对最后两个参数的灵活性较低,即mv [OPTIONS] SOURCE DESTINATION.

为了掌握命令及其参数,您可以使用man(示例:)man lsinfo(示例:)info ls

在包括 C/C++ 在内的许多语言中,您有一种方法可以解析用户附加到可执行文件(命令)调用的命令行参数。还有许多可用于此任务的库,因为在其核心中,正确地完成它实际上并不容易,同时提供了大量的参数及其种类:

  • getopt
  • argp_parse
  • gflags
  • ...

每个 C/C++ 应用程序都有所谓的入口点,这基本上是您的代码开始的地方 -main函数:

int main (int argc, char *argv[]) { // When you launch your application the first line of code that is ran is this one - entry point
    // Some code here
    return 0; // Exit code of the application - exit point
}

无论您是使用库(例如我提到的上述库之一;但这显然在您的情况下是不允许的;))还是自己执行,您的main函数都有两个参数:

  • argc- 表示参数的数量
  • argv- 指向字符串数组的指针(您也可以看到char** argv它基本相同但更难使用)。

注意: main实际上还有第三个参数char *envp[],它允许将环境变量传递给你的命令,但这是一个更高级的东西,我真的不认为你的情况需要它。

命令行参数的处理由两部分组成:

  1. 标记化-这是每个参数都有意义的部分。它是将参数列表分解为有意义的元素(令牌)的过程。在的情况下,ls -l不仅l是一个有效的字符,而且本身也是一个标记,因为它代表一个完整的、有效的参数。

这是一个示例,如何输出参数的数量和可能实际上是或可能不是参数的(未经检查的有效性)字符:

#include <iostream>
using std::cout;
using std::endl;

int main (int argc, char *argv[]) {
    cout << "Arguments' count=%d" << argc << endl;

    // First argument is ALWAYS the command itself
    cout << "Command: " << argv[0] << endl;

    // For additional arguments we start from argv[1] and continue (if any)
    for (int i = 1; i < argc; i++) {
        cout << "arg[" << i << "]: " << argv[i] << endl;
    }

    cout << endl;
    return 0;
}
  1. 解析- 获取令牌(参数及其值)后,您需要检查您的命令是否支持这些。例如:

    user@computer:~$ ls -y
    

    将返回

    ls: invalid option -- 'y'
    Try 'ls --help' for more information.
    

    这是因为解析失败。为什么?因为y(和-y_ -_ _ it) 是命令的未知参数。--:ls

对于每个参数(如果这样被成功识别),您会触发应用程序中的某种更改。您可以使用if-else例如检查某个参数是否有效以及它的作用,然后更改您希望该参数在执行其余代码时更改的任何内容。您可以采用旧的 C 风格或 C++ 风格:

* `if (strcmp(argv[1], "x") == 0) { ... }` - compare the pointer value
* `if (std::string(argv[1]) == "x") { ... }` - convert to string and then compare

我实际上喜欢(当不使用库时)转换argv为这样std::vector的字符串:

std::vector<std::string> args(argv, argv+argc);
for (size_t i = 1; i < args.size(); ++i) {
    if (args[i] == "x") {
        // Handle x
    }
    else if (args[i] == "y") {
        // Handle y
    }
    // ...
}

std::vector<std::string> args(argv, argv+argc);部分只是处理字符串数组的一种更简单的 C++ 方式,因为char *它是一个 C 样式的字符串(char *argv[]作为此类字符串的数组),可以很容易地转换为 C++ 字符串,即std::string. 然后我们可以通过给出起始地址argv然后也指向它的最后一个地址来将所有转换的字符串添加到一个向量中argv + argc(我们将argc字符串的数量添加到它的基地址argv基本上指向我们数组的最后一个地址)。

for上面的循环中,您可以看到我检查(使用 simple if-else)某个参数是否可用,如果是,则相应地处理它。提醒一句:通过使用这样的循环,参数的顺序无关紧要。正如我在开头提到的,一些命令实际上对它们的部分或全部参数有严格的顺序。您可以通过手动调用每个内容args(或者argv如果您使用初始char* argv[]而不是矢量解决方案)以不同的方式处理此问题:

// No for loop!
if (args[1] == "x") {
    // Handle x
}
else if (args[2] == "y") {
    // Handle y
}
// ...

这样可以确保在位置上1只有x预期等。问题是您可以通过超出索引范围来射击自己的腿,因此您必须确保您的索引保持在设置的范围内argc

if (argc > 1 && argc <= 3) {
    if (args[1] == "x") {
        // Handle x
    }
    else if (args[2] == "y") {
        // Handle y
    }
}

上面的示例确保您在索引处有内容,12没有超出。

最后但并非最不重要的一点是,每个参数的处理完全取决于您。您可以使用在检测到某个参数时设置的布尔标志(例如:if (args[i] == "x") { xFound = true; }稍后在您的代码中根据bool xFound及其值执行某些操作),如果参数是数字或由数字和参数名称组成的数字类型(例如:mycommand -x=4有一个参数-x=4,您可以另外解析为x4最后一个是 的值x)等。根据手头的任务,您可能会发疯并为您的命令行参数增加疯狂的复杂性。

希望这可以帮助。如果有不清楚的地方或者您需要更多示例,请告诉我。

于 2016-09-15T08:08:53.563 回答
3

argstream非常类似于boost.program_option:它允许将变量绑定到选项等。但是它不处理存储在配置文件中的选项。

于 2008-10-31T14:26:29.213 回答
3

如果您可以使用 Boost 库,我会推荐 boost::program_options。

在 STL 和常规的 C++/C 运行时库中没有什么特别的。

于 2009-05-14T20:50:00.533 回答
3

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

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

2010 年 10 月 26 日 - 新版本 2.0rc。许多错误已修复,源代码、文档、示例和注释的完整重构已得到纠正。

于 2010-05-03T10:54:27.567 回答
3

您可以为此使用已创建的库

http://www.boost.org/doc/libs/1_44_0/doc/html/program_options.html

于 2010-09-30T00:35:51.490 回答
2

提升 program_options

于 2009-09-17T03:19:19.767 回答
2

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

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

2010 年 10 月 26 日 - 新版本 2.0rc。许多错误已修复,源代码、文档、示例和注释的完整重构已得到纠正。

于 2010-05-03T10:50:04.113 回答
2

这是我最喜欢的命令行方式,尤其是,但绝对不仅仅是在效率成为问题时。这似乎有点矫枉过正,但我​​认为这种矫枉过正几乎没有缺点。

使用 gperf 进行高效的 C/C++ 命令行处理

缺点:

  • 您必须首先运行一个单独的工具来生成 C/C++ 中哈希表的代码
  • 不支持特定的命令行界面。例如,用一个破折号声明多个选项的 posix 速记系统“-xyz”很难实现。

好处:

  • 您的命令行选项与您的 C++ 代码分开存储(在单独的配置文件中,不需要在运行时读取,只需在编译时读取)。
  • 您在代码中所拥有的只是一个开关(打开枚举值)来确定您有哪个选项
  • 效率为 O(n),其中 n 是命令行上的选项数量,可能选项的数量无关紧要。最慢的部分可能是 switch 的实现(有时编译器倾向于像 else 块一样实现它们,从而降低了它们的效率,尽管如果您选择连续值,这不太可能,请参阅:这篇关于 switch 效率的文章
  • 为存储关键字而分配的内存恰好足以容纳关键字集,并且不会更大。
  • 也适用于 C

使用像 eclipse 这样的 IDE,您可能可以自动化运行 gperf 的过程,所以您唯一需要做的就是在配置文件和 switch 语句中添加一个选项,然后按 build...

我使用批处理文件来运行 gperf 并进行一些清理并使用 sed 添加包含防护(在 gperf 生成的 .hpp 文件上)...

因此,您的软件中的代码极其简洁和干净,并且您不需要手动更改一个自动生成的哈希表文件。我怀疑 boost::program_options 是否真的会击败它,即使没有效率作为优先事项。

于 2010-06-21T11:54:55.790 回答
2

如果这是 linux/unix,那么使用的标准是 gnu getopt

http://www.gnu.org/s/libc/manual/html_node/Getopt.html

于 2010-09-30T00:37:33.423 回答
2

我在一些项目中使用了 GetPot:http: //getpot.sourceforge.net/

主要特点:一切都在一个头文件中,没有构建麻烦。只需将它保存在您机器上的某个位置,然后在您的文件中“#include”它main()

最近没有更新,但它有很好的文档记录,并且运行良好。

于 2011-04-20T19:31:45.317 回答
1

你可以试试我的小选项头(166 loc 很容易破解)options.hpp。它是一个单头实现,应该按照您的要求进行。它还会自动打印帮助页面。

于 2014-01-14T10:52:57.133 回答
1

根据我的评论和 rbaleksandar 的回答,传递给 C 中任何程序的参数都是字符串值。为您提供参数 count ( ),它为您提供从零开始argc的参数索引,以当前正在运行的程序的名称(始终为)开头。这将所有参数保留为用户为您的程序提供的参数。每个都将是一个包含在参数向量中的字符串(它是一个指向字符串数组的指针,您将看到写成,或等效地作为函数参数)每个字符串都可供您使用,您只需要测试哪个参数是哪个。argv[0]1 - argcchar *argv[]char **argvargv[1]argv[argc-1]

这将允许您将它们分开,并将它们作为命令( cmd)、选项( opt) 以及最后的参数( arg) 提供给您的cmd.

现在值得注意的是,shell 的规则(bash 等)适用于传递给程序的参数,分词路径名变量扩展在代码获取参数之前应用。因此,您必须考虑是否需要在您的任何参数周围使用引号或更常见的双引号,以防止正常的 shell 拆分,否则将适用(例如ls -al my file.txt,会导致4用户为您的代码提供参数,而ls -al "my file.txt"ls -al my\ file.txt会导致3你的期待。

把所有这些放在一起,你的一小段解析可以像下面这样完成。(您也可以随意使用 aswitch而不是嵌套if的 s 等...)

#include <stdio.h>

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

    char *cmd = NULL,   /* here, since you are using the arguments  */
         *opt = NULL,   /* themselves, you can simply use a pointer */
         *arg = NULL;   /* or the argument itself without a copy    */

    /* looping using the acutal argument index & vector */
    for (int i = 1; i < argc; i++) {
        if (*argv[i] != '-') {      /* checking if the 1st char is - */
            if (!cmd)               /* cmd is currently NULL, and    */
                cmd = argv[i];      /* no '-' it's going to be cmd   */
            else                    /* otherwise, cmd has value, so  */
                arg = argv[i];       /* the value will be opt        */
        }
        else                /* here the value has a leading '-', so  */
            opt = argv[i];  /* it will be the option */
    }

    printf ("\n cmd : %s\n opt : %s\n arg : %s\n\n",
            cmd, opt, arg);

    return 0;
}

示例使用/输出

如果你运行代码,你会发现它为参数提供了分隔,并提供了单独的指针以方便它们的使用:

$ ./bin/parse_cmd ls -la ./cs3000

 cmd : ls
 opt : -la
 arg : ./cs3000

(重要的是要注意,如果您的任务是构建一个命令字符串,您需要将多个值复制到sayoptarg数组而不是开始的指针,或者您可以根据需要动态分配存储空间,例如malloccalloc和/或realloc。然后您将拥有可用于复制和连接值的存储空间。)

如果这是您的挑战,那么在此处的所有答案之间,您应该掌握如何解决您的问题。如果您必须走得更远,并且实际上让您的程序cmd使用opt和执行arg,那么您将需要查看fork生成一个半独立的进程,在该进程中您将执行您的cmd optarg类似于execvor的东西execvp。祝你好运,如果您还有其他问题,请发表评论。

于 2016-09-15T08:57:23.710 回答
1

它有点太大,无法包含在 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;

、和类模板在基类的范围内声明Argument,您可以将它们专门用于您自己的类型。每个都带有指针、选项名称、默认值和用于打印命令概要/用法的描述。OptionAliasCommandLinethis

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

一旦你有了一个实例,就会有几个方法重载来解析来自字符串或main-style 参数向量的输入。解析器处理 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 行(不包括测试)。实例有点臃肿,因为解析表是实例的一部分(而不是类)。但是通常每个程序只需要一个实例,这种纯声明式方法的便利性非常强大,只需解析新输入即可重置实例。

于 2018-04-03T04:23:08.447 回答
0

如果你不想使用 boost,我推荐这个小助手类。

于 2009-01-14T06:47:12.260 回答
0

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

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

2010 年 10 月 26 日 - 新版本 2.0rc。许多错误已修复,源代码、文档、示例和注释的完整重构已得到纠正。

于 2010-10-27T04:04:21.823 回答
0

你的 C/C++ 程序总是有一个 main 函数。它看起来像这样:

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

这里 argc 是一些命令行参数,它们已经传递给你的程序,而 argv 是一个包含这些参数的字符串数组。所以命令行被调用者进程分隔成参数(这不是一行,就像在 Windows 中一样)。

现在你需要整理它们:

  • 命令名称始终是第一个参数(索引 0)。
  • 选项只是指定程序应该如何工作的特殊参数。按照惯例,它们从 - 符号开始。通常 - 一个字母选项和 - 任何更长的时间。所以在你的任务中,“选项”都是参数,从 - 开始,而不是第 0 个。
  • 论据。只是所有其他不是程序名称或选项的参数。
于 2016-09-15T07:09:44.760 回答
0

我在 windows/mingw 下使用 getopt() :

while ((c = getopt(myargc, myargv, "vp:d:rcx")) != -1) {
        switch (c) {
        case 'v': // print version
            printf("%s Version %s\n", myargv[0], VERSION);
            exit(0);
            break;
        case 'p': // change local port to listen to
            strncpy(g_portnum, optarg, 10);
            break;
...
于 2017-12-12T11:32:24.577 回答
-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: }

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

于 2021-02-10T23:14:23.553 回答