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

prog [-abc] [input [output]]

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


相关:

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


当前回答

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

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

其他回答

GNU顶。

一个使用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。

有很多好的库可用。

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

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

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

尝试CLPP库。它是用于命令行参数解析的简单而灵活的库。仅头部和跨平台。仅使用ISO c++和Boost c++库。恕我直言,这比Boost.Program_options简单。

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

2010年10月26日-新发布2.0rc。修正了许多bug,完整的源代码重构、文档、示例和注释都得到了修正。

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

命令基本上是一个字符串。通常它可以分为两部分——命令的名称和命令的参数。

例子:

ls

用于列出目录的内容:

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

上面的ls是在用户的主文件夹中执行的。在这里,要列出哪个文件夹的参数隐式添加到命令中。我们可以显式地传递一些参数:

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
...

哦,尺寸看起来很奇怪(215867,268800)。让我们为人性化输出添加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,我们会得到相同的输出),使用short(通常是一个字母,但有时更多;缩写)或长名称(对于ls,我们有-a或——all用于列出所有文件,包括隐藏文件,——all是-a的长名称)等。有些命令的参数顺序非常重要,但也有其他命令的参数顺序根本不重要。

例如,如果我使用ls -lh或ls -hl并不重要,但在mv(移动/重命名文件)的情况下,你的最后2个参数mv [OPTIONS] SOURCE DESTINATION的灵活性较低。

为了掌握命令及其参数,可以使用man(例如:man ls)或info(例如:info ls)。

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

getopt argp_parse gflags ...

每个C/ c++应用程序都有所谓的入口点,基本上就是你的代码开始的地方——主函数:

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
}

无论你是否使用库(就像我上面提到的其中一个;但这显然是不允许在你的情况下;))或自己做,你的主函数有两个参数:

Argc -表示参数的个数 Argv -一个指向字符串数组的指针(你也可以看到char** Argv,它基本相同,但更难使用)。

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

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

标记化-这是每个参数获得含义的部分。它是将参数列表分解为有意义的元素(标记)的过程。在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;
}

Parsing - after acquiring the tokens (arguments and their values) you need to check if your command supports these. For example: user@computer:~$ ls -y will return ls: invalid option -- 'y' Try 'ls --help' for more information. This is because the parsing has failed. Why? Because y (and -y respectively; note that -, --, : etc. is not required and its up to the parsing of the arguments whether you want that stuff there or not; in Unix/Linux systems this is a sort of a convention but you are not bind to it) is an unknown argument for the ls command.

对于每个参数(如果成功识别),您将在应用程序中触发某种更改。例如,您可以使用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
    }
    // ...
}

The std::vector<std::string> args(argv, argv+argc); part is just an easier C++-ish way to handle the array of strings since char * is a C-style string (with char *argv[] being an array of such strings) which can easily be converted to a C++ string that is std::string. Then we can add all converted strings to a vector by giving the starting address of argv and then also pointing to its last address namely argv + argc (we add argc number of string to the base address of argv which is basically pointing at the last address of our array).

Inside the for loop above you can see that I check (using simple if-else) if a certain argument is available and if yes then handle it accordingly. A word of caution: by using such a loop the order of the arguments doesn't matter. As I've mentioned at the beginning some commands actually have a strict order for some or all of their arguments. You can handle this in a different way by manually calling the content of each args (or argv if you use the initial char* argv[] and not the vector solution):

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

上面的示例确保在索引1和索引2处有内容,但没有超出索引1和索引2的内容。

Last but not least the handling of each argument is a thing that is totally up to you. You can use boolean flags that are set when a certain argument is detected (example: if (args[i] == "x") { xFound = true; } and later on in your code do something based on the bool xFound and its value), numerical types if the argument is a number OR consists of number along with the argument's name (example: mycommand -x=4 has an argument -x=4 which you can additionally parse as x and 4 the last being the value of x) etc. Based on the task at hand you can go crazy and add an insane amount of complexity to your command line arguments.

希望这能有所帮助。如果有什么不清楚或者你需要更多的例子,请告诉我。