【问题标题】:Command line parser for Qt4Qt4 的命令行解析器
【发布时间】:2009-05-15 18:50:15
【问题描述】:

我正在寻找 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 样式的许可证,因此大多数人都可以使用该代码。

标签: command-line qt4 parsing


【解决方案1】:

QCoreApplication's constructors 需要(int &argc, char **argv)(并且QApplication 继承自QCoreApplication)。作为documentation states,强烈推荐

由于 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. 上发布的内容?)。

【讨论】:

  • 扯掉 kdelibs...多么好的主意,我怎么没想到呢?我将不得不对此进行测试!
  • 我将我的代表点扔到屏幕上,但没有任何反应。
【解决方案2】:

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

【讨论】:

    【解决方案3】:

    这或多或少与 ehemient 相同的答案,但使用简单的正则表达式来帮助解析 args。 (如果您只需要少量 args,这种方法可能很有用)

    用这个运行:

    ./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 - 我添加了一些正则表达式来阐明这个例子。
    【解决方案4】:

    还有来自http://www.libqxt.org/的QxtCommandOptions

    【讨论】:

    • QxtCommandOptions 似乎没有包含在 qxt 0.6 或提示中。
    • @maxschlepzig: 标题在那里——可能是一些文档生成器错误。
    【解决方案5】:

    该软件包通过 opts.addSwitch("disable-foo", &foo_disabled); 和 opts.addSwitch("enable- foo", &foo_enabled);。您需要同时检查两者,并与指定两者的人打交道(哎呀)。

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

    【讨论】:

    • 这意味着我需要为每个 bool 添加两个规则。不是最佳实践。此外,我想要一个“原生”Qt4 解决方案。例如,添加多个值的开关时,我想得到一个QList,或者说我想要一个点时,我想得到一个QPoint。 opts.addPoint("位置", myQPointVariable)
    • 是的,没错,你需要两个“规则”——但你也有两个选择;它是 --disable-foo 不是 --foo=disabled。如果两者都有,则需要同时检测启用和禁用时的错误(尽管这可以在 getopt 解析器中完成)。 ehemient 的答案有更多特定于 QT 的信息,但他建议的库具有与 froglogic 相同的一般限制。 QT arguments() 东西不能处理任何你想要的解析。
    【解决方案6】:

    【讨论】:

    • 太好了!!!看起来像 BSD'ish 许可证,纯 Qt ......但它有点冗长。例如,处理命令行 agruments 所需的代码量:code.google.com/p/qtargparser/source/browse/trunk/samples/help/… 我确实需要找时间玩弄它。谢谢!
    • 看起来相当不错,但如果您看到整个 API 都使用异常,则不然。我不想在不使用异常的框架中使用异常。 :(
    【解决方案7】:

    一个非常简单的方法是扫描“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) ??
    

    添加 25nov --

    // 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 的回答
    • 更喜欢将其命名为 ArgTest, extern ArgTest argTest;
    【解决方案8】:

    现在是 2013 年,仍然没有“第一方”arg 解析器。无论如何..如果有人发现自己面临同样的问题并希望避免 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` 
    

    参数可以按任意顺序传递

    编辑:此 sn-p 的更新将是 found here

    【讨论】:

      【解决方案9】:

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

      【讨论】:

      • 作为 LGPL 允许人们使用它而无需开源他们的代码。我错过了什么吗?
      • LGPL 需要动态链接,这并不总是可行的,也不是一个好主意。
      【解决方案10】:

      对于一些花哨的选项解析,您可以尝试gperf。

      IBM 上有一个不错的 tutorial。

      【讨论】:

      • GPL,而不是 LGPL/MIT/X11.... 并且没有与 Qt4“原语”集成,请参阅我对 jesup 的评论
      • 我不太确定该许可证是否适用于 gperf 生成的代码,据我所知,您并没有链接到 gperf。它只是构建查找函数/对象的外部工具。平心而论,我还没有将它用于实际项目。
      【解决方案11】:

      我也在寻找这样做时遇到的另一个选项:

      http://code.google.com/p/qgetopts/

      我没用过。

      【讨论】:

        猜你喜欢
        • 2019-07-14
        • 2011-01-10
        • 2012-07-27
        • 1970-01-01
        • 2010-11-27
        • 2014-01-04
        • 2013-07-12
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多