如果程序被指定像这样运行,那么在c++中解析命令行参数的最佳方式是什么:
prog [-abc] [input [output]]
是否有某种方法可以在标准库中实现这一点,或者我需要自己编写代码?
相关:
在unicode c++应用程序中解析命令行参数
如果程序被指定像这样运行,那么在c++中解析命令行参数的最佳方式是什么:
prog [-abc] [input [output]]
是否有某种方法可以在标准库中实现这一点,或者我需要自己编写代码?
相关:
在unicode c++应用程序中解析命令行参数
当前回答
一个简单的解决方案是将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: }
这种方法肯定有很大的局限性,但我发现它很好地平衡了简单性和实用性。
其他回答
尝试CLPP库。它是用于命令行参数解析的简单而灵活的库。仅头部和跨平台。仅使用ISO c++和Boost c++库。恕我直言,这比Boost.Program_options简单。
图书馆:http://sourceforge.net/projects/clp-parser
2010年10月26日-新发布2.0rc。修正了许多bug,完整的源代码重构、文档、示例和注释都得到了修正。
命令基本上是一个字符串。通常它可以分为两部分——命令的名称和命令的参数。
例子:
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.
希望这能有所帮助。如果有什么不清楚或者你需要更多的例子,请告诉我。
提振。Program_options
还有另一种选择是精益平均c++选项解析器:
http://optionparser.sourceforge.net
它是一个只包含头文件的库(实际上只有一个头文件),与所有其他建议不同 也是独立的,即它没有任何依赖关系。特别是,它不依赖于STL。它甚至不使用异常或任何其他需要库支持的东西。这意味着它可以与普通C语言或其他语言链接,而无需引入“外国”库。
像boost::program_options一样,它的API提供了方便的直接访问选项, 也就是说,你可以这样写代码
如果 (选项[帮助]) ... ;
and
int VERBOSE = options[VERBOSE].count();
然而,与boost::program_options不同的是,这只是使用一个带有(用户提供的)enum索引的数组。这提供了没有权重的关联容器的便利。
它有良好的文档记录,并具有公司友好的许可证(MIT)。
tlmc++ OP包含了一个很好的格式化程序,用于使用可以执行的消息 行换行和列对齐在本地化程序时非常有用,因为它可以确保即使在具有较长消息的语言中输出也会很好。它还省去了手动格式化80列的麻烦。
有很多好的库可用。
Boost Program Options是一个相当重量级的解决方案,因为将它添加到项目中需要构建Boost,而且语法有点令人困惑(在我看来)。但是,它几乎可以做任何事情,包括让命令行选项覆盖配置文件中设置的选项。
SimpleOpt是一个相当全面但简单的命令行处理器。它是一个单一的文件,具有简单的结构,但只处理将命令行解析为选项,您必须进行所有的类型和范围检查。它适用于Windows和Unix,并且还附带了一个适用于Windows的glob版本。
getopt在Windows上可用。它与Unix机器上的相同,但它通常是一个GPL库。