在 C++ 中解析命令行参数? [关闭]
Posted
技术标签:
【中文标题】在 C++ 中解析命令行参数? [关闭]【英文标题】:Parsing Command Line Arguments in C++? [closed] 【发布时间】:2010-09-20 04:39:03 【问题描述】:如果程序被指定为这样运行,那么在 C++ 中解析命令行参数的最佳方法是什么:
prog [-abc] [input [output]]
标准库中是否有一些方法可以做到这一点,还是我需要编写自己的代码?
相关:
Parsing command line arguments in a unicode C++ application【问题讨论】:
我认为 Nunit 源代码 (C#) 有一个很好的命令行处理类示例...... 最简单的方法是使用参数解析库之一:getopt
或 argparse
。
如果你不能使用库(例如 boost),至少使用std::vector<std::string> args(argv, argv+argc);
这样你就可以解析字符串向量而不是字符数组数组。
对于已经在他们的程序中使用 OpenCV 的人,cv::CommandLineParser 也可能是一个不错的选择。 [我的意思是,以防万一您已经将它用于其他目的,我的意思并不是要将 OpenCV 包含在命令行解析器中。]
最近为现代 c++ 写了这个:github.com/juzzlin/Argengine
【参考方案1】:
Boost.Program_options
【讨论】:
这似乎是 C++ 最明显的选择,但它的文档还不够完整。尝试在那里找到如何存储和检索文件中的选项,这是一项基本功能。我不喜欢使用它的代码的外观,特别是options.add_options()(option1)(option2)...
的措辞,我认为这是对 C++ 语法的滥用。
使用 Boost.Program_options 编译代码似乎并不简单,除了包含头文件之外,还需要链接选项等。
您可以花更少的钱获得几乎相同的东西。如果你想要--long-option
之类的东西,你可以自己动手。
在一种极端情况下,对于非常简单的程序,或者您只是直接使用 argv[] 数组。另一种情况,为了让你的参数具有完全的灵活性,你可以直接使用 argv 数组(你可以做 prog -1 firstinput -2 second input -obj constructor arguments ..)。否则,请使用 boost、tclap 或许多其他方法。
boost::program_options
是无可救药的过度设计、难以使用和文档不足。为数不多的从完全重新设计和重写中受益匪浅的 Boost 库之一。如果可以避免,请不要使用它。【参考方案2】:
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。
【讨论】:
呃。我了解在 C 代码中使用这个库,但是 IMO,这太低级了,在我写过的任何 C++ 应用程序中都无法接受。如果您不需要纯 C,请寻找更好的库。 还有一个 getopt 的 GNU 示例,其值为例如myexe -c myvalue 可以尝试搜索“Example of Parsing Arguments with getopt” 这是一个由 GNU 自己提供的example 示例^^【参考方案3】:GNU C 库中有these 工具,其中包括GetOpt。
如果你使用 Qt 并且喜欢 GetOpt 接口,froglogic 已经发布了一个不错的接口here。
【讨论】:
【参考方案4】:argstream
与boost.program_option
非常相似:它允许将变量绑定到选项等。但是它不处理存储在配置文件中的选项。
【讨论】:
【参考方案5】:还有一个Google library 可用。
真的,命令行解析已经“解决”了。随便挑一个吧。
【讨论】:
(感谢@QPaysTaxes 注意到链接已损坏;我不知道您的编辑为什么被拒绝,但您绝对是正确的)。 我想不出对问题的帮助不大的回答。 '解决了。选一个。'对不起,但是“呃”。用我大约 15 分钟的时间思考这个问题,我想出了大约 30 种不同的场景来解决这个问题。我怀疑“正确”的响应更类似于解释一组特定的关注点如何导致一组特定的代码实现。但是,嘿,谢谢你的来电。【参考方案6】:对于 C++,答案通常在 Boost...
Boost.Program Options
【讨论】:
【参考方案7】:我建议使用库。有经典而受人尊敬的getopt,我相信还有其他人。
【讨论】:
或者 getopt_long 如果你有它 我不确定 getopt 在 Windows 上是否可用。我知道它在 GNU 上的 C 语言中 我的同事在 Windows 上使用它。我很确定它在 Windows 上可用【参考方案8】:如果你不想使用 boost,我推荐this little helper class。
【讨论】:
【参考方案9】:如果你只想自己处理命令行选项,最简单的方法是:
vector<string> args(argv + 1, argv + argc);
在您的main()
顶部。这会将所有命令行参数复制到std::string
s 的向量中。然后您可以使用==
轻松比较字符串,而不是无休止的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
- 已修复。]
【讨论】:
添加了一个简单的例子。实际上,vectormycommand.exe -h file.csv
,我想告诉他们他们没有正确使用该实用程序以及原因(如果他们只是使用该版本,则不应提供文件名)。这个例子相当简单,但我可以想到更复杂的标志。最终结果将是:有时顺序确实很重要,有时则不重要。那么......我应该如何进行呢?如果您对我的问题有任何疑问,请告诉我。
@Hamish:我有点困惑——将字符串加载到向量中不会“丢失”它们的顺序。您仍然可以使用args[i]
访问第 i 个参数(实际上我自己经常这样做,正如我的代码 sn-p 中的注释所说)。如果您一次只需要处理一个,迭代器样式会更方便一些。这能回答你的问题吗?【参考方案10】:
有许多不错的库可用。
Boost Program Options 是一个相当重量级的解决方案,因为将其添加到您的项目中需要您构建 boost,并且语法有些混乱(在我看来)。但是,它几乎可以做任何事情,包括让命令行选项覆盖配置文件中设置的选项。
SimpleOpt 是一个相当全面但简单的命令行处理器。它是一个单独的文件,结构简单,但只处理将命令行解析为选项,您必须进行所有类型和范围检查。它适用于 Windows 和 Unix,并且还附带了适用于 Windows 的 glob 版本。
getopt 在 Windows 上可用。它和 Unix 机器上的一样,但它通常是一个 GPL 库。
【讨论】:
【参考方案11】:Boost.Program_options 应该可以解决问题
【讨论】:
不错的选择。或者,如果您由于某种原因不能使用 boost,那么基于标准 c 的“getopt”函数也可以完成工作。 boost::program_options 的文档可能会更完整。很难找到如何使用文件来保留选项,这是一个关键功能。 只为了解析命令行选项而在代码库中引入增强功能有点“敲竹杠”。如果 boost 已经存在,请使用它。否则看看像 gopt 这样的东西。总的来说,没有什么不反对 boost,但它有点重量级,我发现这些版本与 g++ 版本紧密相关。 此外,boost::program_options 不是一个只有头文件的库。你必须建立提升。这很麻烦。 对于这项任务来说,提升似乎完全是矫枉过正【参考方案12】:您可以使用 GNU GetOpt (LGPL) 或各种 C++ 端口之一,例如 getoptpp (GPL)。
使用 GetOpt 的简单示例(prog [-ab] 输入)如下:
// 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;
【讨论】:
仅供参考,GNU getopt 是 GPL 而 getoptpp 也是 GPL,因此 boost 变体可能更适合非开源软件。 @SorinSbarnea, TINLA,但我相信许可证是 actually LGPLv2。 很抱歉,但项目页面上的 Google 代码许可证清楚地说明了 GPL。 @SorinSbarnea,你看我的链接了吗?我应该说得更清楚些,但我指的是 getopt 和 getopt-gnu,而不是 getoptpp。 Getopt 对于 C++ 程序来说是愚蠢的低级。我强烈建议不要将它用于 C++ 程序。【参考方案13】:如果您可以使用 Boost 库,我建议您使用 boost::program_options。
在 STL 和常规 C++/C 运行时库中没有任何特定内容。
【讨论】:
【参考方案14】: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;
【讨论】:
这是开箱即用的。但是请注意,选项参数是const std::string&
。重要的是 std::find
的 value 参数是 std::string
以便使用 std::string::operator==()
而不是 char * operator==()
(因为后者只会比较指针值而不是字符串内容)。
这不能按预期工作,例如,像 tar 应用程序:tar -xf file
,对吧?每个选项都必须分开。 grep -ln pattern file
不会被理解,但必须是 grep -l -n pattern file
。
绝对如果你想要posix风格的命令行选项,那么你应该使用其他答案中提到的命令行处理库之一。正如这个答案所说,这是用于简单的命令行选项。
这很好,但需要进行两个小改进:首先,构造函数参数应该是 const 限定的,其次,getCmdOption 的返回值应该是一个值,而不是一个引用,否则你会遇到@987654321 @。除此之外,这是一个不错且简单的解决方案,我会使用它,谢谢。
1. @iain 那段代码的许可证是什么? 2. @TomášDvořák 关于返回对临时的引用是正确的,当getCmdOption
返回空字符串时。 InputParser
应该有一个 std::string empty_string
作为成员,并在找不到选项时返回其引用。【参考方案15】:
试试Boost::Program Options。它允许您读取和解析命令行以及配置文件。
【讨论】:
它是否允许您显示当前值和默认值?几年前,我不得不围绕它实现一个包装器来显示有效的选项(基于解析的配置/参数,而不仅仅是显示提供的字符串)。【参考方案16】:我喜欢 C 的 getopt(),但是我老了。 :-)
【讨论】:
【参考方案17】:Boost program_options.
【讨论】:
【参考方案18】:谷歌的gflags
【讨论】:
看起来很适合简单使用。对于小型工具,我一直很喜欢“作为全局变量定义的选项”。仅基元 + 字符串,这可能是一个很大的减号。【参考方案19】:试试 CLPP 库。它是用于命令行参数解析的简单灵活的库。仅标题和跨平台。仅使用 ISO C++ 和 Boost C++ 库。恕我直言,它比 Boost.Program_options 更容易。
图书馆:http://sourceforge.net/projects/clp-parser
2010 年 10 月 26 日 - 新版本 2.0rc。修复了许多错误,对源代码、文档、示例和 cmets 进行了全面重构。
【讨论】:
【参考方案20】:试试 CLPP 库。它是用于命令行参数解析的简单灵活的库。仅标题和跨平台。仅使用 ISO C++ 和 Boost C++ 库。恕我直言,它比 Boost.Program_options 更容易。
图书馆:http://sourceforge.net/projects/clp-parser/
2010 年 10 月 26 日 - 新版本 2.0rc。修复了许多错误,对源代码、文档、示例和 cmets 进行了全面重构。
【讨论】:
【参考方案21】:这是我最喜欢的命令行方式,尤其是在效率成为问题的情况下。这可能看起来有点矫枉过正,但我认为这种矫枉过正的缺点很少。
Use gperf for efficient C/C++ command line processing
缺点:
您必须先运行一个单独的工具才能在 C/C++ 中生成哈希表的代码 不支持特定的命令行界面。例如,用一个破折号声明多个选项的 posix 速记系统“-xyz”很难实现。优点:
您的命令行选项与您的 C++ 代码分开存储(在单独的配置文件中,不需要在运行时读取,只需在编译时读取)。 您的代码中只有一个开关(打开枚举值)来确定您有哪个选项 效率为 O(n),其中 n 是命令行上的选项数,可能的选项数无关紧要。最慢的部分可能是 switch 的实现(有时编译器倾向于像 else 块一样实现它们,这会降低它们的效率,尽管如果您选择连续值则不太可能,请参阅:this article on switch efficiency) 分配用于存储关键字的内存恰好足以容纳关键字集,并且不会更大。 也适用于 C使用像 eclipse 这样的 IDE,您可能可以自动化运行 gperf 的过程,所以您唯一需要做的就是在配置文件和 switch 语句中添加一个选项,然后按 build...
我使用批处理文件来运行 gperf 并进行一些清理并使用 sed 添加包含保护(在 gperf 生成的 .hpp 文件上)...
因此,您的软件中的代码极其简洁明了,而且您无需手动更改一个自动生成的哈希表文件。我怀疑 boost::program_options 是否真的会击败它,即使没有效率作为优先事项。
【讨论】:
【参考方案22】:您可能希望为此使用外部库。有很多可供选择。
Boost 有一个功能非常丰富(和往常一样)的库Boost Program Options。
过去几年我个人最喜欢的是TCLAP——纯模板化,因此没有库或链接、自动“--help”生成和其他好东西。请参阅文档中的 simplest example。
【讨论】:
+1,不了解 tclap,它设法轻量级,但感觉很完整,我肯定会更深入地研究。【参考方案23】:您可以为此使用已创建的库
http://www.boost.org/doc/libs/1_44_0/doc/html/program_options.html
【讨论】:
【参考方案24】:如果这是 linux/unix,那么标准的使用是 gnu getopt
http://www.gnu.org/s/libc/manual/html_node/Getopt.html
【讨论】:
问题不是关于 C++ 的,Getopt 只是普通的 C。曾经有它的 C++ 变体,但由于某种原因它被撤回了。 它在 c++ 中运行良好;它是我们在所有 c++ 代码中使用的。 嗯,是的,但你可以做得更好,例如拍拍。我用新的选项定义添加或删除了一行,我不需要在其他地方编辑代码——>老派 getopt 不是这样。【参考方案25】:试试 CLPP 库。它是用于命令行参数解析的简单灵活的库。仅标题和跨平台。仅使用 ISO C++ 和 Boost C++ 库。恕我直言,它比 Boost.Program_options 更容易。
图书馆:http://sourceforge.net/projects/clp-parser
2010 年 10 月 26 日 - 新版本 2.0rc。修复了许多错误,对源代码、文档、示例和 cmets 进行了全面重构。
【讨论】:
【参考方案26】:我在一些项目中使用了 GetPot: http://getpot.sourceforge.net/
主要特点:一切都在一个头文件中,没有构建麻烦。只需将其保存在您机器上的某个位置,然后将其“#include”到您的文件中,其中包含main()
最近没有更新,但文档很好,运行良好。
【讨论】:
它从函数返回false,应该返回char*,这怎么可能? (函数是内联的 const char* GetPot::__match_starting_string(const char* StartString)) @Asalle 不知道,但这似乎是一个“内部”函数,而不是 API 的一部分。如有疑问,最好询问作者。【参考方案27】:我发现ezOptionParser 更容易使用。它也是一个单独的头文件,除了 STL 之外不依赖任何东西,适用于 Windows 和 Linux(很可能也适用于其他平台),由于示例而没有学习曲线,具有其他库不具备的功能(如文件导入/导出带有 cmets、带有分隔符的任意选项名称、自动使用格式等),并且是 LGPL 许可的。
【讨论】:
从 0.1.3 版开始,许可证现在是 MIT。我正在一个新项目而不是 TCLAP 上尝试这个,到目前为止它看起来很有希望。文件配置选项非常好。 我刚刚试用了exOptionParser,但它有很多问题。首先,我收到 58 条关于 unsigned int 到 int 转换的警告。它还尝试增加列表迭代器(不能像那样使用)并崩溃。它的界面也很糟糕。它在所有地方都使用引用,而不仅仅是返回您想要的数据。尽管它是在 C++ STL 之上构建的,但它看起来像一个 C 库。 注意;检测未知参数不起作用。此外,如果没有放在其他标头之前,标头会产生编译错误。我会寻找另一个解析器..【参考方案28】: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);
【讨论】:
-1:这会在没有绑定检查的情况下获取数组元素 -1:因为没有边界检查 @RobertMunafo:对argv[i+1]
的引用很容易超出argv
数组的范围。考虑使用 "-i"
作为 last 参数运行程序。
-1 是一个“自己动手”的答案,而该问题专门要求一个“STL 中的库”来完成该任务。此外,正如 Keith Thompson 和 mmutz 所指出的,边界检查存在一些错误。
我发现 cmets 真的很苛刻。我认为还可以展示一个示例,说明如何在不使用库的情况下完成此操作。这个答案是补充 +1,对不起。【参考方案29】:
AnyOption 是一个 C++ 类,用于轻松解析复杂的命令行选项。它还以选项值对格式解析来自 rsourcefile 的选项。
AnyOption 实现了传统的 POSIX 样式字符选项 (-n) 以及较新的 GNU 样式长选项 (--name)。或者,您可以通过要求忽略 POSIX 样式选项来使用更简单的长选项版本 ( -name )。
【讨论】:
类似于“The Lean Mean C++ Option Parser”,但有更好的 API。强烈推荐。 它不支持Windows以外的任何其他操作系统 @Asalle 是什么让你说它只是 Windows? 它使用 strcpy_s。通过编写我自己的调用 strcpy 的又快又脏的 strcpy_s,我很幸运地让它在 macos 上工作。【参考方案30】:我可以建议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;
【讨论】:
这个选项对我来说是最简单的,尽管它确实为我的程序添加了一个包含许多头文件的子目录。需要相应地编辑包含路径。 这些年来我使用过各种解决方案,包括我自己自制的解决方案。我和其他人一起赞美 TCLAP 的美德。它很容易集成并满足我的需求。 看来项目已移至sourceforge。 @JohnShedletsky 你确定吗?我不再使用该库,但在 manual 中显示了长格式和短格式参数。 没有投反对票,但我个人不喜欢这种依赖异常的小东西。以上是关于在 C++ 中解析命令行参数? [关闭]的主要内容,如果未能解决你的问题,请参考以下文章