处理命令行参数的设计模式是啥
Posted
技术标签:
【中文标题】处理命令行参数的设计模式是啥【英文标题】:What is the design pattern for processing command line arguments处理命令行参数的设计模式是什么 【发布时间】:2010-09-08 10:32:35 【问题描述】:如果您正在编写一个可从命令行执行的程序,您通常希望为用户提供多个选项或标志,以及可能不止一个参数。我已经绊倒了很多次,但是是否有某种设计模式可以循环遍历 args 并调用适当的处理函数?
考虑:
myprogram -f filename -d directory -r regex
在使用语言的任何内置函数检索参数后,如何组织处理函数? (欢迎使用特定语言的答案,如果这有助于您清晰地表达答案)
【问题讨论】:
您编写了一个程序,要求用户输入位置或关键字和必需或可选参数。然后,您的程序需要对这些提交的参数的某些组合/排列采取行动。您是否询问设计模式来构建根据给定输入完成不同任务的功能?或者您是在询问循环和解析参数的设计模式(例如,您是在询问由解析器库(如 C 的 getopt 和 Python 的 argparse)实现的设计模式)? 【参考方案1】:我认为以下答案更符合您的要求:
你应该看看应用模板模式(“设计模式”[Gamma, el al] 中的模板方法)
总之它的整体处理是这样的:
If the arguments to the program are valid then
Do necessary pre-processing
For every line in the input
Do necessary input processing
Do necessary post-processing
Otherwise
Show the user a friendly usage message
简而言之,实现一个包含以下方法的 ConsoleEngineBase 类:
PreProcess()
ProcessLine()
PostProcess()
Usage()
Main()
然后创建一个机箱,实例化一个 ConsoleEngine() 实例并发送 Main() 消息以启动它。
要查看如何将其应用于控制台或命令行程序的一个很好的示例,请查看以下链接: http://msdn.microsoft.com/en-us/magazine/cc164014.aspx
这个例子是用 C# 编写的,但是这些想法很容易在任何其他环境中实现。
您会将 GetOpt() 视为适合参数处理(预处理)的部分。
希望这会有所帮助。
【讨论】:
赞成坚持概念而不是实施。我觉得这应该是选择的答案。【参考方案2】:我不知道任何记录在案的处理“模式”。
我相信用于处理参数的最古老的库/API 之一是 getopt。谷歌搜索“getopt”会显示很多手册页和实现的链接。
通常,我的应用程序中有一个参数处理器知道如何与之通信的首选项或设置服务。然后将参数转换为该服务中的应用程序然后查询的内容。这可以像设置字典一样简单(例如名为“文件名”的字符串设置)。
【讨论】:
【参考方案3】:您没有提到该语言,但对于 Java,我们很喜欢 Apache Commons CLI。对于 C/C++,getopt。
【讨论】:
【参考方案4】:嗯,这是一个旧帖子,但我仍然想贡献。该问题旨在选择设计模式,但是我可以看到很多关于使用哪个库的讨论。我已经根据 lindsay 查看了 microsoft 链接,该链接讨论了要使用的模板设计模式。
但是,我不相信这篇文章。模板模式的目的是定义一个模板,该模板将由各种其他类实现以具有统一的行为。我不认为解析命令行适合它。
我宁愿采用“命令”设计模式。这种模式最适合菜单驱动的选项。
http://www.blackwasp.co.uk/Command.aspx
所以在您的情况下,-f、-d 和 -r 都成为定义了通用或单独接收器的命令。这样以后可以定义更多的接收器。下一步将链接这些命令的职责,以防需要处理链。我会选择。
http://www.blackwasp.co.uk/ChainOfResponsibility.aspx
我想这两者的结合最适合组织命令行处理或任何菜单驱动方法的代码。
【讨论】:
-f, -d, -r
似乎指定了单个ConcreteCommand
的信息。如果用户指定这些标志 -f -d
的另一种组合,例如,(或 just -f
或 just -r
或 just @ 987654328@,那么它们中的每一个都可以代表另一个ConcreteCommand
,其功能(由Execute()
实现)不同于用-f, -d, -r
初始化的ConcreteCommand
。【参考方案5】:
关于这个的几个cmet......
首先,虽然本身没有任何模式,但编写解析器本质上是一种机械练习,因为给定语法,可以轻松生成解析器。想到了 Bison 和 ANTLR 等工具。
也就是说,解析器生成器对于命令行来说通常是多余的。所以通常的模式是自己写一个(正如其他人已经证明的那样)几次,直到你厌倦了处理繁琐的细节并找到一个库来为你做。
我为 C++ 编写了一个,它节省了 getopt 带来的大量工作并很好地利用了模板:TCLAP
【讨论】:
TCLAP
是一个很棒的 CLI 解析库。规则的预解析设置和 argv
解析的解析后提取非常有用、直观,有助于将程序分解为正确的离散组件 (恕我直言)。【参考方案6】:
boost::program_options 库非常适合您使用 C++ 并且可以使用 Boost。
【讨论】:
是个不错的选择。与旧的 getopt 相比,该库有点复杂,但也允许使用配置文件(替换或集成命令行参数)。【参考方案7】:假设您有一个“配置”对象,您打算使用标志和合适的命令行解析器来设置该对象,该解析器负责解析命令行并提供恒定的选项流,这里有一段伪代码
while (current_argument = cli_parser_next())
switch(current_argument)
case "f": //Parser strips the dashes
case "force":
config->force = true;
break;
case "d":
case "delete":
config->delete = true;
break;
//So on and so forth
default:
printUsage();
exit;
【讨论】:
【参考方案8】:我更喜欢“-t text”和“-i 44”等选项;我不喜欢“-fname”或“--very-long-argument=some_value”。
而“-?”、“-h”和“/h”都会产生帮助屏幕。
我的代码如下所示:
int main (int argc, char *argv[])
int i;
char *Arg;
int ParamX, ParamY;
char *Text, *Primary;
// Initialize...
ParamX = 1;
ParamY = 0;
Text = NULL;
Primary = NULL;
// For each argument...
for (i = 0; i < argc; i++)
// Get the next argument and see what it is
Arg = argv[i];
switch (Arg[0])
case '-':
case '/':
// It's an argument; which one?
switch (Arg[1])
case '?':
case 'h':
case 'H':
// A cry for help
printf ("Usage: whatever...\n\n");
return (0);
break;
case 't':
case 'T':
// Param T requires a value; is it there?
i++;
if (i >= argc)
printf ("Error: missing value after '%s'.\n\n", Arg);
return (1);
// Just remember this
Text = Arg;
break;
case 'x':
case 'X':
// Param X requires a value; is it there?
i++;
if (i >= argc)
printf ("Error: missing value after '%s'.\n\n", Arg);
return (1);
// The value is there; get it and convert it to an int (1..10)
Arg = argv[i];
ParamX = atoi (Arg);
if ((ParamX == 0) || (ParamX > 10))
printf ("Error: invalid value for '%s'; must be between 1 and 10.\n\n", Arg);
return (1);
break;
case 'y':
case 'Y':
// Param Y doesn't expect a value after it
ParamY = 1;
break;
default:
// Unexpected argument
printf ("Error: unexpected parameter '%s'; type 'command -?' for help.\n\n", Arg);
return (1);
break;
break;
default:
// It's not a switch that begins with '-' or '/', so it's the primary option
Primary = Arg;
break;
// Done
return (0);
【讨论】:
【参考方案9】:我正在重复 mes5k 的 ANTLR 答案。此link to Codeproject 用于讨论 ANLTR 并使用访问模式来实现您希望应用程序执行的操作的文章。写得很好,值得回顾。
【讨论】:
【参考方案10】:我建议使用命令行处理器库。 Some Russian guy 创造了一个不错的,但那里有很多。将为您节省一些时间,这样您就可以专注于应用程序的用途,而不是解析命令行开关!
【讨论】:
【参考方案11】:Getopt 是唯一的方法。
http://sourceforge.net/projects/csharpoptparse
【讨论】:
【参考方案12】:解释器模式怎么样? http://www.dofactory.com/net/interpreter-design-pattern
【讨论】:
【参考方案13】:您没有为此提及一种语言,但如果您正在寻找一个非常好的围绕 getopt 的 Objective-C 包装器,那么 Dave Dribin 的 DDCLI 框架非常好。
http://www.dribin.org/dave/blog/archives/2008/04/29/ddcli
【讨论】:
【参考方案14】:我在 perl 中使用 Getopts::std 和 Getopts::long 以及在 C 中使用 Getopt 函数。这标准化了参数的解析和格式。其他语言有不同的机制来处理这些问题。
希望对你有帮助
【讨论】:
【参考方案15】:标准设计通常遵循 getopt 所做的,有许多语言的 getopt 库,.NET、python、C、Perl、php 等。
基本设计是有一个命令行解析器,它部分返回传递给循环检查的参数。
This 文章更详细地讨论了它。
【讨论】:
以上是关于处理命令行参数的设计模式是啥的主要内容,如果未能解决你的问题,请参考以下文章