Qt4的命令行解析器

24

我正在寻找适用于Qt4的命令行解析器。

我进行了一次小型的谷歌搜索,并发现这个网址:http://www.froglogic.com/pg?id=PublicationsFreeware&category=getopt,然而它不支持"--enable-foo"和"--disable-foo"开关。除此之外,它看起来像是一个真正的胜利者。

编辑:

看来Frologic已经删除了该项目。所以我最好的选择是使用Boost(它不是API或ABI稳定的),或者为kdelibs提供支持。耶...


对于那些(像我一样)仍在使用Qt4的人,可以通过Internet Archive Wayback机器获取frologic库。frologic代码的许可证是3条款的BSD风格许可证,因此大多数人都可以使用该代码。 - Michael Kohne
11个回答

23

QCoreApplication的构造函数需要(int &argc, char **argv)QApplicationQCoreApplication继承而来)。正如文档所述,强烈建议在应用程序本身对argv进行解释或修改之前,先创建 QApplication ,因为它还处理常见的命令行参数。

由于 QApplication 还处理常见的命令行参数,所以通常最好在应用程序本身对 argv 进行解释或修改之前创建它。

如果你让Qt来处理参数的第一步,那么最好使用QStringList QCoreApplication::arguments()而不是遍历argv。因为QApplication可能会移除一些它自己使用的参数。

这不太适用于其他参数解析库...

不过,kdelibs附带了一个很好的参数解析器KCmdLineArgs。它是LGPL许可证,如果你真的想要,可以不使用KApplication(调用KCmdLineArgs::init)。

KCmdLineOptions options;
options.add("enable-foo", ki18n("enables foo"));
options.add("nodisable-foo", ki18n("disables foo"));
// double negatives are confusing, but this makes disable-foo enabled by default

KCmdLineArgs::addCmdLineOptions(options);
KApplication app;
KCmdLineArgs *args = KCmdLineArgs::parsedArgs();

if (args->isSet("enable-foo") && !args->isSet("disable-foo"))
    cout << "foo enabled" << endl;
else
    cout << "foo disabled" << endl;

未经测试(谁会测试他们在S.O.上发布的内容?)。


3
抄袭 kdelibs... 真是个好主意,我为什么没想到呢?我得测试一下! - elcuco
我把我的声望点数扔到屏幕上,但什么都没有发生。 - UmNyobe

18

自Qt 5.2以来,您终于可以在QtCore本身中找到解决方案:我在那里贡献了QCommandLineParser


9

这个回答和ephemient的回答差不多,但是使用了一个简单的正则表达式来帮助解析参数。(如果你只需要少数几个参数,这种方法可能很有用)

使用以下命令运行:

./QArgTest --pid=45 --enable-foo

代码如下:

int main(int argc, char *argv[]) {
    QApplication app(argc, argv, false);
    qDebug() << "QApp arg test app"; 

    QStringList args = app.arguments();

    int pid = 0;

    QRegExp rxArgPid("--pid=([0-9]{1,})");
    QRegExp rxArgFooEna("--enable-foo");
    QRegExp rxArgFooDis("--disable-foo");

    for (int i = 1; i < args.size(); ++i) {
        if (rxArgPid.indexIn(args.at(i)) != -1 ) {   
            pid =  rxArgPid.cap(1).toInt();
            qDebug() << i << ":" << args.at(i) << rxArgPid.cap(1) << pid;
        }
        else if (rxArgFooEna.indexIn(args.at(i)) != -1 ) {   
            qDebug() << i << ":" << args.at(i) << "Enable Foo";
        } 
        else if (rxArgFooDis.indexIn(args.at(i)) != -1 ) {   
            qDebug() << i << ":" << args.at(i) << "Disable Foo";
        } 
        else {
            qDebug() << "Uknown arg:" << args.at(i);
        }
    }
    return 0;
}

你如何扩展这个程序以支持多个命令参数?需要更多的正则表达式吗? - retrodrone
@retrodrone - 我增加了一些正则表达式来澄清这个例子。 - Johan

7

1
QxtCommandOptions似乎没有包含在qxt 0.6或tip中。 - maxschlepzig
1
@maxschlepzig:头文件已经在那里了——可能是一些文档生成器的错误。 - alexei

3

这个软件包确实支持通过 opts.addSwitch("disable-foo", &foo_disabled);opts.addSwitch("enable-foo", &foo_enabled); 来禁用和启用 foo。你需要处理两种情况的检查,并处理有人同时指定两种情况(糟糕)。

我不明白这与 QT4 有什么关系...


这意味着对于每个布尔值,我需要添加两个规则。这不是最佳实践。此外,我想要一个“本地”的Qt4解决方案。例如,当添加具有多个值的开关时,我希望获得QList,或者当说我想要一个点时,我希望获得QPoint。opts.addPoint(“location”,myQPointVariable) - elcuco
是的,没错,你需要两个“规则”——但你也有两个选项;它是--disable-foo而不是--foo=disabled。如果两者都有,你需要检测并至少在启用和禁用同时出现时报错(尽管这可以在getopt解析器中完成)。ephemient的答案有更多QT特定的信息,但他建议的库与froglogic的限制相同。QT arguments()的东西无法处理任何你想要的解析类型。 - jesup

2
一个非常简单的方法是扫描 "key=value" 参数,将它们放入一个表中,比如说 zz.map: QString -> QVariant,并使用 zz.map.value(key, default) 来获取它们的值。 以下是一个示例:
#include "ztest.h"
Ztest zz;  
int main( int argc, char* argv[] )
{
    zz.eqargs( ++ argv );  // scan  test=2 x=str ... to zz.map

    QString xx = zz.map.value( "xx", "" );
    if( Zint( Size, 10 ))  // a #def -> zz.map.value( "Size", 10 )
        ...

ztest.h文件小于1页,Python相关内容不超过10行。

每个人都有自己喜欢的选项解析器;而这个是最简单的之一。
值得重提的是:无论您如何指定选项,请将它们回显到输出文件中--
"我认识的每个科学家都很难跟踪上次运行脚本时使用的参数"。

为了使QPoints等工作,当然需要一个QString -> QPoint解析器。 有没有人知道为什么这在Qt 4.4.3中不起作用?

QPoint pt(0,0);
QDataStream s( "QPoint(1,2)" );
s >> pt;
qDebug() << "pt:" << pt;  // QPoint(1364225897,1853106225) ??

新增 11月25日 --

// ztest.h: scan args x=2 s=str ... to a key -> string table
// usage:
// Ztest ztest;
// int main( int argc, char* argv[] )
// {
//     QApplication app( argc, argv );
//     ztest.eqargs( ++ argv );  // scan leading args name=value ...
//     int x = Zint( x, 10 );  // arg x= or default 10
//     qreal ff = Zreal( ff, 3.14 );
//     QString s = Zstr( s, "default" );
// care: int misspelled = Zint( misspellled ) -- you lose
//version: 2009-06-09 jun denis

#ifndef ztest_h
#define ztest_h

#include <QHash>
#include <QString>
#include <QVariant>
#include <QRegExp>

//------------------------------------------------------------------------------
class Ztest {
public:
  QHash< QString, QVariant > map;
  int test;  // arg test=num,  if( ztest.test )

  Ztest() : test( 0 ) {}

  QVariant val( const QString& key, const QVariant& default_ = 0 )
  {
    return map.value( key, default_ );
  }

  void setval( const QString& key, const QVariant& val )
  {
    map[key] = val;
    if( key == "test"  ||  key == "Test" )
        test = val.toInt();
  }

//------------------------------------------------------------------------------
    // ztest.eqargs( ++ argv )  scans test=2 x=3 ... -> ztest table
  void eqargs( char** argv )
  {
    char** argv0 = argv;
    char *arg;
    QRegExp re( "(\\w+)=(.*)" );  // name= anything, but not ./file=name
    for( ; (arg = *argv) && re.exactMatch( arg );  argv ++ ){
        setval( re.cap(1), re.cap(2) );
    }
        // change argv[0..] -> args after all name=values
    while(( *argv0++ = *argv++) != 0 ) {}
  }
};

extern Ztest ztest;

    // macros: int x = Zint( x, 10 ): x= arg or default 10
#define Zstr( key, default )    ztest.val( #key, default ).toString()
#define Zint( key, default )    ztest.val( #key, default ).toInt()
#define Zreal( key, default )   ztest.val( #key, default ).toDouble()

#endif

丹尼斯,我在哪里可以获取“ztest”类?请看Muby的回答。 - elcuco
1
请将其翻译为:偏好命名为ArgTest,外部ArgTest argTest; - raidsan

2

时光荏苒,现在已经是2013年了,但依然没有“第一方”命令行参数解析器。无论如何...如果有人和我一样遇到了同样的问题,并想要避免学习cmd解析器库所需要面对的学习曲线,那么这里有一个“快速&脏”的解决方案:-

QString QArgByKey(QString key, QChar sep = QChar('\0') ) //prototype usually in separate header

QString QArgByKey(QString key, QChar sep )
{
    bool sepd=sep!=QChar('\0');
    int pos=sepd?qApp->arguments().indexOf(QRegExp('^'+key+sep+"\\S*")):qApp->arguments().indexOf(QRegExp(key));
    return pos==-1?QString::null:
    (sepd?qApp->arguments().at(pos).split(sep).at(1):(++pos<qApp->arguments().size()?qApp->arguments().at(pos):QString::null));
}

例子:

user@box:~$ ./myApp  firstKey=Value1 --secondKey Value2 thirdKey=val3.1,val3.2,val3.3 --enable-foo

用法:

QString param1   = QArgByKey("firstkey",'='); // Returns `Value1` from first pair
QString param2   = QArgByKey("--secondkey"); // Returns `Value2` from second pair
QString param3-1 = QArgByKey("thirdkey",'=').split(',').at(0); // Returns `val3.1`
bool fooEnabled  = qApp->arguments().contains("--enable-foo"); //To check for `--enable-foo` 

参数可以以任何顺序传递

编辑:此代码片段的更新将在此处找到


2

1
太好了!看起来像是BSD许可证,纯Qt……但是有点啰嗦。比如,看看处理命令行参数所需的代码量:http://code.google.com/p/qtargparser/source/browse/trunk/samples/help/main.cpp我需要找时间来试试它。谢谢! - elcuco
看起來相當不錯,但如果你看到整個 API 使用了 Exception.. 我不希望在一個不使用 Exception 的框架中使用它們。 :( - Tobias

1

它必须是Qt4特定的吗?如果不是,GNU Getopt非常好用,尽管许可证可能会成为问题,如果您不是在开源软件中使用。


1
作为LGPL授权,允许使用它而无需开源自己的代码。我有什么遗漏的吗? - tshepang
1
LGPL要求动态链接,这并不总是可行或明智的选择。 - Zifre

0

如果你想要一些高级的选项解析,可以尝试使用gperf

IBM有一个很好的tutorial关于它。


GPL,但不是LGPL/MIT/X11……也不与Qt4的“基元”集成。请参见我对Jesup的评论。 - elcuco
我不太确定许可证是否适用于由gperf生成的代码,据我所知,您没有链接到gperf。它只是一个外部工具,用于构建查找函数/对象。公平地说,我还没有在实际项目中使用过它。 - Marius Or.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接