How can I format a std::string using a collection of arguments?

大憨熊 提交于 2020-06-09 16:49:48

问题


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);

回答1:


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.




回答2:


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.




回答3:


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?



来源:https://stackoverflow.com/questions/5076472/how-can-i-format-a-stdstring-using-a-collection-of-arguments

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!