【问题标题】:Extract parameters from string, included quoted regions, in Qt在 Qt 中从字符串中提取参数,包括带引号的区域
【发布时间】:2026-01-10 20:55:01
【问题描述】:

我有一个 Qt5/C++ 程序,它接收一个包含程序名称和可能参数的 QString。我需要将此 QString 拆分为多个字符串。例如,字符串

/tmp/myprog --param1 --param2=2 --param3="1 2 3" -p 4

应该拆分成:

list[0]=/tmp/myprog
list[1]=--param1
list[2]=--param2=2
list[3]=--param3="1 2 3"
list[4]=-p 4

我的第一个想法是在空格上使用“split”方法,但这会破坏引用的 param3。同样,参数 4 在 -p 和 4 之间也没有 : 或 =。

有没有简单的方法来做到这一点?我怀疑 -p 4 不可能轻易拆分,因为无法知道以下两项是否在一起。

但是是否有拆分/正则表达式/其他方式来拆分上述内容? (如果我们忽略 -p 4 可能吗?)


更新:

相同的函数可以在没有--param 或-p 的情况下拆分字符串吗?所以一串

abc "a a" "b b b"  c

会变成

list[0]=abc
list[1]="a a"
list[2]="b b b"
list[3]=c

【问题讨论】:

  • 也许你可以在这里参与QCommandLineParser

标签: c++ regex qt split


【解决方案1】:

你的任务有两个方面:

  1. 将命令行拆分为参数。这需要从头开始实施。

  2. 处理参数以提取参数及其值。从 Qt 5.2 开始,您可以使用 QCommandLineParser 来执行此操作。

#include <QCoreApplication>
#include <QCommandLineParser>
#include <QDebug>

static QStringList splitCommandLine(const QString & cmdLine)
{
    QStringList list;
    QString arg;
    bool escape = false;
    enum { Idle, Arg, QuotedArg } state = Idle;
    foreach (QChar const c, cmdLine) {
        if (!escape && c == '\\') { escape = true; continue; }
        switch (state) {
        case Idle:
            if (!escape && c == '"') state = QuotedArg;
            else if (escape || !c.isSpace()) { arg += c; state = Arg; }
            break;
        case Arg:
            if (!escape && c == '"') state = QuotedArg;
            else if (escape || !c.isSpace()) arg += c;
            else { list << arg; arg.clear(); state = Idle; }
            break;
        case QuotedArg:
            if (!escape && c == '"') state = arg.isEmpty() ? Idle : Arg;
            else arg += c;
            break;
        }
        escape = false;
    }
    if (!arg.isEmpty()) list << arg;
    return list;
}

int main(int argc, char * argv[])
{
    QCoreApplication app(argc, argv);
    QCommandLineParser parser;
    parser.addHelpOption();
    QCommandLineOption param1("param1");
    QCommandLineOption param2("param2", "", "val2");
    QCommandLineOption param3("param3", "", "val3");
    QCommandLineOption param4("p", "", "val4");
    parser.addOption(param1);
    parser.addOption(param2);
    parser.addOption(param3);
    parser.addOption(param4);
    if (true) {
        // Parse a string
        QString cmdLine("/tmp/myprog --param1 --param2=2\\ 2 --param3=\"1 2 3\" -p 4");
        parser.parse(splitCommandLine(cmdLine));
    } else {
        // Parse a command line passed to this application
        parser.process(app);
    }
    if (parser.isSet(param1)) qDebug() << "param1";
    if (parser.isSet(param2)) qDebug() << "param2:" << parser.value(param2);
    if (parser.isSet(param3)) {
        QStringList values = parser.value(param3)
                .split(' ', QString::SkipEmptyParts);
        qDebug() << "param3:" << values;
    }
    if (parser.isSet(param4)) qDebug() << "param4:" << parser.value(param4);
    return 0;
}

输出:

param1
param2: "2 2"
param3: ("1", "2", "3")
param4: "4"

QDebug 引用它输出的字符串。字符串本身不包含任何引号。

【讨论】:

  • 您确定 param2 的输出吗?如果正确,我不确定我是否理解原因。
  • @GenerationDSystems 好吧,看看我作为输入传递的内容。这是故意的,以表明转义是正确的。
  • 我用'/usr/bin/logger "customstart" "param 2"' 尝试了你的代码并得到了输出("/usr/bin/logger"、"customstartparam"、"2")。我会深入挖掘,但我怀疑有些地方不太对劲。
  • @GenerationDSystems 我在您的参数中看不到任何破折号。也许您的意思是/usr/bin/logger --customstart --param 2,那么您还需要将customstartparam 作为参数添加到解析器。它工作得很好。 "--param 2" 周围的引号无效并且不应该工作,因为空格不是参数名称的一部分。像您那样引用意味着param 2 是完整的命令选项名称,其中包含空格。实施对您的问题是正确的。如果您需要其他内容,请修改问题。
  • @GenerationDSystems 当按照您的说明传递输入时,splitCommandLine 会生成以下三个字符串:("/usr/bin/logger", "customstart", "param 2")。这是正确的,而不是您得到的输出。当然,命令行解析器不知道以后如何处理它。
【解决方案2】:

我不熟悉 Qt 的细节,但是快速查看 QString 的文档here,我相信以下代码应该工作(尽管效率低下)。

#include<QString>
#include<vector>

vector<QString> list; //vector used for variable length parameter requirements
QString args; //The arguments string
bool quoteFlag = false;
for(int i=0; i<args.size(); i++){
    QString qstr = "";
    while(args.at(i) != QChar(' ') || quoteFlag){ //loops if character is not ' ' or is within a set of quotes
        if(args.at(i) == QChar('\"'){
            quoteFlag = !quoteflag; //basically says, "you are now within a set of quotes"
        }
        qstr += args.at(i); //add current character to qstr
        i++
    }
    list.push_back(qstr) //add qstr to the argument list
}

再次重申,我不使用任何 Qt 库,并且此代码未经测试,但是,如果我理解文档,它应该工作。

-p 4 位而言,这对于其他参数的当前语法是不可能的。

【讨论】:

  • 我认为这会起作用 - 但我的直觉告诉我使用 Qt 库有一些简单的东西......但我找不到它。我也想知道是否有一个正则表达式可以智能地对引号中的项目进行分组,等等......
  • 有一个 split() 的正则表达式版本,但是,我有点没时间写这个了,但是如果我可以为你设计一个,我会稍微编辑一下我的答案并把它在那里。
  • 经过长时间的考虑和测试,我找不到在不严重改变结果字符串的情况下在参数之间拆分字符串的方法。抱歉,但我认为在这些情况下无法使用正则表达式。
【解决方案3】:

在Qt中从字符串中提取参数,包括带引号的区域

在解析任何“转义”或引用的字符串时:splitArgs(来自 KF5)非常有用。

以下源代码管理“转义”等;来自

    one two a\ b "c d e"

打印出来

    one
    two
    a b
    c d e

源代码的核心只是一个splitArgs调用:

#include <QTextStream>
#include <KShell>

int main()
{
    QString whole = "one two a\\ b \"c d e\"";
    QTextStream(stdout) << whole << endl << "--------------------" << endl;

    QStringList group = KShell::splitArgs("one two a\\ b \"c d e\"");
    for (auto element: group) {
        QTextStream(stdout) << element << endl;
    }
}

【讨论】:

    最近更新 更多