Is it possible to format std::string
passing a set of arguments?
Currently I am formatting the string this way:
string helloString = "Hello %s and %s";
vector<string> tokens; //initialized vector of strings
const char* helloStringArr = helloString.c_str();
char output[1000];
sprintf_s(output, 1000, helloStringArr, tokens.at(0).c_str(), tokens.at(1).c_str());
But the size of the vector is determined at runtime. Is there any similar function to sprintf_s
which takes a collection of arguments and formats a std::string/char*? My development environment is MS Visual C++ 2010 Express.
EDIT: I would like to achieve something similar:
sprintf_s(output, 1000, helloStringArr, tokens);
The most C++-ish way to achieve sprintf-like functionality would be to use stringstreams.
Here is an example based on your code:
#include <sstream>
// ...
std::stringstream ss;
std::vector<std::string> tokens;
ss << "Hello " << tokens.at(0) << " and " << tokens.at(1);
std::cout << ss.str() << std::endl;
Pretty handy, isn't it ?
Of course, you get the full benefit of IOStream manipulation to replace the various sprintf flags, see here http://www.fredosaurus.com/notes-cpp/io/omanipulators.html for reference.
A more complete example:
#include <string>
#include <sstream>
#include <iostream>
#include <iomanip>
int main() {
std::stringstream s;
s << "coucou " << std::setw(12) << 21 << " test";
std::cout << s.str() << std::endl;
return 0;
}
which prints:
coucou 21 test
Edit:
As pointed by the OP, this way of doing things does not allow for variadic arguments, because there is no `template' string built beforehand allowing the stream to iterate over the vector and insert data according to placeholders.
You can do it with the Boost.Format library, because you can feed the arguments one by one.
This actually enables you to achieve your goal, quite unlike the printf
family where you have to pass all the arguments at once (i.e you'll need to manually access each item in the container).
Example:
#include <boost/format.hpp>
#include <string>
#include <vector>
#include <iostream>
std::string format_range(const std::string& format_string, const std::vector<std::string>& args)
{
boost::format f(format_string);
for (std::vector<std::string>::const_iterator it = args.begin(); it != args.end(); ++it) {
f % *it;
}
return f.str();
}
int main()
{
std::string helloString = "Hello %s and %s";
std::vector<std::string> args;
args.push_back("Alice");
args.push_back("Bob");
std::cout << format_range(helloString, args) << '\n';
}
You can work from here, make it templated etc.
Note that it throws exceptions (consult documentation) if the vector doesn't contain the exact amount of arguments. You'll need to decide how to handle those.
The boost::format library might be of interest if you want to avoid having to manually deal with the output buffer.
As for taking the plain vector as input, what would you want to happen if tokens.size()<2
? Wouldn't you have to ensure that the vector was big enough to index elements 0 and 1 in any case?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With