我们需要始终格式化字符串。能说:
真是太好了std::string formattedStr = format("%s_%06d.dat", "myfile", 18); // myfile_000018.dat
有这样的C ++方法吗?我考虑过的一些替代方案:
snprintf
:使用原始char
缓冲区。在现代C ++代码中不太好。std::stringstream
:不支持格式模式字符串,而是必须将笨拙的iomanip对象推送到流中。boost::format
:使用%
的ad-hoc运算符重载来指定参数。难看。现在我们有C ++ 11,现在还有更好的方法来使用可变参数模板吗?
答案 0 :(得分:12)
它当然可以使用可变参数模板在C ++ 11中编写。最好包装已存在的东西,而不是试图自己编写整个东西。如果您已经在使用Boost,那么将boost::format
包裹起来非常简单:
#include <boost/format.hpp>
#include <string>
namespace details
{
boost::format& formatImpl(boost::format& f)
{
return f;
}
template <typename Head, typename... Tail>
boost::format& formatImpl(
boost::format& f,
Head const& head,
Tail&&... tail)
{
return formatImpl(f % head, std::forward<Tail>(tail)...);
}
}
template <typename... Args>
std::string format(
std::string formatString,
Args&&... args)
{
boost::format f(std::move(formatString));
return details::formatImpl(f, std::forward<Args>(args)...).str();
}
您可以按照您想要的方式使用它:
std::string formattedStr = format("%s_%06d.dat", "myfile", 18); // myfile_000018.dat
如果你不想使用Boost(但你真的应该),那么你也可以包裹snprintf
。由于我们需要管理char缓冲区和旧式非类型安全可变长度参数列表,因此它更容易涉及并且容易出错。使用unique_ptr
:
#include <cstdio> // snprintf
#include <string>
#include <stdexcept> // runtime_error
#include <memory> // unique_ptr
namespace details
{
template <typename... Args>
std::unique_ptr<char[]> formatImplS(
size_t bufSizeGuess,
char const* formatCStr,
Args&&... args)
{
std::unique_ptr<char[]> buf(new char[bufSizeGuess]);
size_t expandedStrLen = std::snprintf(buf.get(), bufSizeGuess, formatCStr, args...);
if (expandedStrLen >= 0 && expandedStrLen < bufSizeGuess)
{
return buf;
} else if (expandedStrLen >= 0
&& expandedStrLen < std::numeric_limits<size_t>::max())
{
// buffer was too small, redo with the correct size
return formatImplS(expandedStrLen+1, formatCStr, std::forward<Args>(args)...);
} else {
throw std::runtime_error("snprintf failed with return value: "+std::to_string(expandedStrLen));
}
}
char const* ifStringThenConvertToCharBuf(std::string const& cpp)
{
return cpp.c_str();
}
template <typename T>
T ifStringThenConvertToCharBuf(T const& t)
{
return t;
}
}
template <typename... Args>
std::string formatS(std::string const& formatString, Args&&... args)
{
// unique_ptr<char[]> calls delete[] on destruction
std::unique_ptr<char[]> chars = details::formatImplS(4096, formatString.c_str(),
details::ifStringThenConvertToCharBuf(args)...);
// string constructor copies the data
return std::string(chars.get());
}
在格式规范方面,snprintf
和boost::format
之间存在一些差异,但您的示例适用于两者。
答案 1 :(得分:1)
fmt library实现了这一点,使用可变参数模板进行字符串格式化。例如:
// printf syntax:
std::string formattedStr = fmt::sprintf("%s_%06d.dat", "myfile", 18);
// Python-like syntax:
std::string formattedStr = fmt::format("{}_{:06}.dat", "myfile", 18);
免责声明:我是图书馆的作者。