41

Everybody loves

QString("Put something here %1 and here %2")
    .arg(replacement1)
    .arg(replacement2);

but things get itchy as soon as you have the faintest chance that replacement1 actually contains %1 or even %2 anywhere. Then, the second QString::arg() will replace only the re-introduced %1 or both %2 occurrences. Anyway, you won't get the literal "%1" that you probably intended.

Is there any standard trick to overcome this?

If you need an example to play with, take this

#include <QCoreApplication>
#include <QDebug>

int main()
{
    qDebug() << QString("%1-%2").arg("%1").arg("foo");
    return 0;
}

This will output

"foo-%2"

instead of

"%1-foo"

as might be expected (not).

    qDebug() << QString("%1-%2").arg("%2").arg("foo");

gives

"foo-foo"

and

    qDebug() << QString("%1-%2").arg("%3").arg("foo");

gives

"%3-foo"
Tilman Vogel
  • 9,337
  • 4
  • 33
  • 32
  • I know, I could just use string concatenation using `operator + ()` but in some places, e.g. translation using `tr()`, one would like to keep the ability to reorder the arguments in the template. – Tilman Vogel Mar 09 '11 at 17:29
  • Wow! I never encountered that case, but if a translation is the string "%1" (opposed to 1%, for example) then it won't work right at all. This really does not make sense. It looks like they reapply the arguments to the result over and over. – Alexis Wilke Nov 14 '15 at 18:28
  • Wasn't there the possibility to pseudo-escape "%1" by writing "%%1"? Still the functionality of using a percent-marker in an arg-call is good. Imagine you have a standard error-string using `%1` to replace the "where" and `%2` to replace "what" - now in some cases you simply replace "where" with a string, in others you'd like a number. In case of the string you'd maybe want to put apostrophes around it, in case of a number a hashtag in front would be good to improve readability. Now you can use the 1st arg-call to replace `%1` with f.e. `"#%3"` and a 3rd arg-call to insert the number. – St0fF Jun 02 '17 at 20:45

3 Answers3

35

See the Qt docs about QString::arg():

QString str;
str = "%1 %2";
str.arg("%1f", "Hello"); // returns "%1f Hello"
Andreas Haferburg
  • 5,189
  • 3
  • 37
  • 63
Johnny
  • 1,966
  • 17
  • 24
  • Ok, thanks! And how to deal with the case that you need different overloads of `QString::arg()`, e.g. for numbers as well? Using `QString::number()` in the multi-QString-`arg()` is not the same thing as it doesn't give you the possibility to specify the field width. – Tilman Vogel Mar 09 '11 at 17:33
  • Ok, I could use `QString::leftJustified()` or `QString::rightJustified()`, but this gets lengthy. I think having an automatically escaping version of `arg()` would be great. – Tilman Vogel Mar 09 '11 at 17:37
  • @Johnny, `QString("%1-%2").arg("%1").arg(23, 8, 10, QChar('0'));` will make `QString("00000023-%2")` instead of `QString("%1-00000023")`. – Tilman Vogel Mar 09 '11 at 17:43
  • @Tilman, what do you think about this: QString("%1-%2").arg("%1", QString("%1").arg(23, 8, 10, QChar('0'))); ? – Johnny Mar 09 '11 at 17:54
  • @Johnny, yeah, not totally terrible ;-) Still sad, it doesn't work in the original elegance... One way would be to escape all % in substitutions and replace them back once the arg() shower has stopped, using some intermediate string-template class. Thanks for your thoughts! – Tilman Vogel Mar 09 '11 at 22:20
8

Note that the arg() overload for multiple arguments only takes QString. In case not all the arguments are QStrings, you could change the order of the placeholders in the format string:

QString("1%1 2%2 3%3 4%4").arg(int1).arg(string2).arg(string3).arg(int4);

becomes

QString("1%1 2%3 3%4 4%2").arg(int1).arg(int4).arg(string2, string3);

That way, everything that is not a string is replaced first, and then all the strings are replaced at the same time.

Andreas Haferburg
  • 5,189
  • 3
  • 37
  • 63
4

You should try using

QString("%1-%2").arg("%2","foo");
Nazik
  • 8,696
  • 27
  • 77
  • 123