14

There is an issue using QString::arg() when a string contains a digit right after a place marker. It's not clear from the QString::arg() function description what would happen in case of such a replacement:

QString("String for replacement %1234").arg("blah");

Will this result in "String for replacement blah234" or "String for replacement blah34"?

I looked in the QT's source code to answer this question. It seems that the algorithm which looks for place markers is 'greedy' and it would take both digits in the example above.

Here is the source of the QT's function which is used inside the QString::arg() (QT 4.8.4):

static ArgEscapeData findArgEscapes(const QString &s)
{
const QChar *uc_begin = s.unicode();
const QChar *uc_end = uc_begin + s.length();

ArgEscapeData d;

d.min_escape = INT_MAX;
d.occurrences = 0;
d.escape_len = 0;
d.locale_occurrences = 0;

const QChar *c = uc_begin;
while (c != uc_end) {
    while (c != uc_end && c->unicode() != '%')
        ++c;

    if (c == uc_end)
        break;
    const QChar *escape_start = c;
    if (++c == uc_end)
        break;

    bool locale_arg = false;
    if (c->unicode() == 'L') {
        locale_arg = true;
        if (++c == uc_end)
            break;
    }

    if (c->digitValue() == -1)
        continue;

    int escape = c->digitValue();
    ++c;

    if (c != uc_end && c->digitValue() != -1) {
        escape = (10 * escape) + c->digitValue();
        ++c;
    }

    if (escape > d.min_escape)
        continue;

    if (escape < d.min_escape) {
        d.min_escape = escape;
        d.occurrences = 0;
        d.escape_len = 0;
        d.locale_occurrences = 0;
    }

    ++d.occurrences;
    if (locale_arg)
        ++d.locale_occurrences;
    d.escape_len += c - escape_start;
}
return d;
}

Is there a better way of solving such an ambiguity than always using a 2-digit place markers?

tshepang
  • 12,111
  • 21
  • 91
  • 136
Oleg Yakovenko
  • 263
  • 3
  • 10
  • 3
    That's an excellent observation, I never thought about this. The only thing I would think to do is `QString("String for replacement %1%2").arg("blah").arg(234);`, but that's pretty ugly. – Taylor Brandstetter May 24 '13 at 15:02

3 Answers3

4

Qt help states for arg(const QString & a, int fieldWidth = 0, QChar fillChar = QLatin1Char( ' ' ))

Returns a copy of this string with the lowest numbered place marker replaced by string a, i.e., %1, %2, ..., %99.

...

Place marker numbers must be in the range 1 to 99.

Therefore, what you're seeing is, by definition, correct; the first two numbers will be replaced. If you're wanting "String for replacement blah234", then you could define the string as: -

QString("String for replacement %1%2").arg("blah").arg(234);
Luc Touraille
  • 79,925
  • 15
  • 92
  • 137
TheDarkKnight
  • 27,181
  • 6
  • 55
  • 85
  • 3
    I'm not saying that something is incorrect. I'm saying that a digit after a place marker creates some ambiguity. There is and extra space between place markers in your solution. In case there is a space, there is no such an issue. – Oleg Yakovenko May 27 '13 at 10:36
4

Since you can only use %1 to %99 as markers and you can skip marker numbers you can write:

QString("String for replacement %10234").arg("blah");

to output String for replacement blah234

Andrey Korneyev
  • 26,353
  • 15
  • 70
  • 71
Frank
  • 56
  • 2
  • 1
    And comment the heck out of it because that will definitely not be intuitive to the next person looking at the code (or yourself 3 years from now). At least it works though. – iamtheddrman Dec 19 '14 at 15:43
1

I have the same issue, but the order answers not looks like a good way for me.

I have resolve the ambiguity in this way.

QString myString= QString("ConcatenatedNumbers%0123").arg(66,3,10, QChar('0'));

The string will be:

ConcatenatedNumbers06623
Mquinteiro
  • 1,034
  • 1
  • 11
  • 31