如果程序被指定像这样运行,那么在c++中解析命令行参数的最佳方式是什么:
prog [-abc] [input [output]]
是否有某种方法可以在标准库中实现这一点,或者我需要自己编写代码?
相关:
在unicode c++应用程序中解析命令行参数
如果程序被指定像这样运行,那么在c++中解析命令行参数的最佳方式是什么:
prog [-abc] [input [output]]
是否有某种方法可以在标准库中实现这一点,或者我需要自己编写代码?
相关:
在unicode c++应用程序中解析命令行参数
当前回答
您可以使用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;
}
其他回答
尝试Boost::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; }
}
提振。Program_options
你可能想要使用一个外部库。有很多选择。
Boost有一个非常丰富的功能(像往常一样)库Boost程序选项。
过去几年我个人最喜欢的是TCLAP——纯粹的模板化,因此没有库或链接,自动生成“——帮助”和其他好东西。请参阅文档中最简单的示例。
命令基本上是一个字符串。通常它可以分为两部分——命令的名称和命令的参数。
例子:
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.
希望这能有所帮助。如果有什么不清楚或者你需要更多的例子,请告诉我。