The function FixMessage below was written to solve the problem of wanting to supply arguments to messages in a language-independent fashion (eg. in some language nouns and verbs may come in different orders), so that doing something like the following would not allow for the message to be translated without needing code changes:
Instead, this system uses the idea of substituting by number (eg. %1 is argument 1, %2 is argument 2), and supplying the arguments as a vector of strings.
The same argument can be re-used (eg. "%1 %1 %1" would give "fish fish fish").
You can insert a % by using %%.
You can have up to 35 replacements by using %1 to %9 and then %A to %Z (not case-sensitive).
If a replacement can't be made (eg. you use %Z and there are not 35 strings in the vector) then the replacement sequence is displayed (eg. %Z) as a warning of what you have omitted.
Output
printf ("The %s is in the %s", "fish", "bag");
Instead, this system uses the idea of substituting by number (eg. %1 is argument 1, %2 is argument 2), and supplying the arguments as a vector of strings.
vector <string> args;
args.push_back ("fish"); // arg %1
args.push_back ("bag"); // arg %2
cout << FixMessage ("The %1 is in the %2", args) << endl;
The same argument can be re-used (eg. "%1 %1 %1" would give "fish fish fish").
You can insert a % by using %%.
You can have up to 35 replacements by using %1 to %9 and then %A to %Z (not case-sensitive).
If a replacement can't be made (eg. you use %Z and there are not 35 strings in the vector) then the replacement sequence is displayed (eg. %Z) as a warning of what you have omitted.
// disable warnings about long names
#ifdef WIN32
#pragma warning( disable : 4786)
#endif
#include <string>
#include <vector>
#include <iostream>
using namespace std;
string FixMessage (const string & msg, const vector<string> args)
{
string result;
string::size_type pos, // where we are now
percent; // where the % is
for (pos = 0;
(percent = msg.find ('%', pos)) != string::npos;
pos = percent)
{
result += msg.substr (pos, percent++ - pos); // before the %
// the % might be the last character, so just return it at the end
if (percent >= msg.size ())
return result + "%";
// get parameter (eg. %1)
char c (msg [percent++]);
// %% represents a single %
if (c == '%')
result += '%';
else
{
// convert into subscript (eg. '1' becomes 1, 'a' becomes 10, and so on).
// cast to unsigned in case it is >= 0x80
unsigned int argnumber = toupper (static_cast<unsigned char> (c));
// a becomes 10, b becomes 11 and so on
if (argnumber >= 'A' && argnumber <= 'Z')
argnumber -= 'A' - 10;
else
argnumber -= '0';
// if character in array put in substitute
if (argnumber >= 1 && argnumber <= args.size ())
result += args [argnumber - 1];
else
result += string ("%") + c; // otherwise put character back
} // end of not %%
} // end of for loop - searching for % symbols
// append rest of string after final argument
result += msg.substr (pos); // rest of string
return result;
} // end of FixMessage
int main (void)
{
vector <string> args;
args.push_back ("fish"); // arg %1
args.push_back ("bag"); // arg %2
cout << FixMessage ("The %1 is in the %2", args) << endl;
cout << FixMessage ("The %2 contains %1 and more %1", args) << endl;
cout << FixMessage ("I sometimes eat 90%% %1", args) << endl;
return 0;
} // end of main
Output
The fish is in the bag
The bag contains fish and more fish
I sometimes eat 90% fish