我想创建一个包含嵌入信息的字符串。实现我想要的一种方式(不是唯一的方法)称为string interpolation或变量替换,其中字符串中的占位符被替换为实际值。
在C中,我会做这样的事情:
printf("error! value was %d but I expected %d",actualValue,expectedValue)
如果我在python中编程,我会做这样的事情:
"error! value was {0} but I expected {1}".format(actualValue,expectedValue)
这两个都是字符串插值的例子。
如何在C ++中执行此操作?
重要提示:
std::cout
:cout << "error! value was " << actualValue << " but I expected "
<< expectedValue;
我不想打印字符串到stdout。我想将std::string
作为参数传递给函数(例如异常对象的构造函数)。
修改
对于我的直接使用,我并不关心性能(我正在大声提出异常!)。 然而,了解各种方法的相对表现一般非常有用。
为什么不直接使用printf(毕竟C ++是C的超集......)? This answer讨论了为什么不这样做的一些原因。根据我的理解,类型安全是一个重要原因:如果你把%d放在那里,你放在那里的变量最好真的可以转换为整数,因为这就是函数如何计算它是什么类型。使用一种方法可以更安全地使用编译时知道要插入的变量的实际类型。
答案 0 :(得分:16)
方法1:使用字符串流
看起来std::stringstream
提供了一个快速解决方案:
std::stringstream ss;
ss << "error! value was " << actualValue << " but I expected " << expectedValue << endl;
//example usage
throw MyException(ss.str())
正
否定
方法2:提升格式
Boost Format库也是可能的。使用它,你会这样做:
throw MyException(boost::format("error! value was %1% but I expected %2%") % actualValue % expectedValue);
正
否定
修改强>
方法3:可变参数模板参数
似乎可以通过使用可变参数模板参数(对于采用无限数量模板参数的模板的技术术语)来创建类型安全的printf版本。我已经看到了这方面的一些可能性:
正
否定
答案 1 :(得分:6)
在C ++ 11中,您可以使用std::to_string
:
"error! value was " + std::to_string(actualValue) + " but I expected " + std::to_string(expectedValue)
它并不漂亮,但它很简单,您可以使用宏来缩小它。性能不是很好,因为事先没有reserve()
空间。 Variadic templates可能会更快,看起来更好。
这种字符串构造(而不是插值)对于本地化也是不好的,但如果需要,你可能会使用库。
答案 2 :(得分:4)
使用你喜欢的任何东西:
1)std :: stringstream
#include <sstream>
std::stringstream ss;
ss << "Hello world!" << std::endl;
throw std::runtime_error(ss.str());
2)libfmt:https://github.com/fmtlib/fmt
#include <stdexcept>
throw std::runtime_error(
fmt::format("Error has been detected with code {} while {}",
0x42, "copying"));
答案 3 :(得分:2)
在C ++ 20中,您将可以使用std::format
。
请参阅http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0645r9.html,以获取接受的论文。
这将支持python样式格式:
string s1 = std::format("{1} to {0}", "a", "b");
已经有一个实现:https://github.com/fmtlib/fmt
答案 4 :(得分:0)
免责声明: 后续代码基于我2年前阅读的一篇文章。我会找到源并将其尽快放在这里。
这就是我在C ++ 17项目中使用的东西。应该可以与任何支持可变参数模板的C ++编译器一起使用。
用法:
std::string const word = "Beautiful";
std::string const message = CString::format("%0 is a %1 word with %2 characters.\n%0 %2 %0 %1 %2", word, "beautiful", word.size());
// Prints:
// Beautiful is a beautiful word with 9 characters.
// Beautiful 9 Beautiful beautiful 9.
该类的实现:
/**
* The CString class provides helpers to convert 8 and 16-bit
* strings to each other or format a string with a variadic number
* of arguments.
*/
class CString
{
public:
/**
* Format a string based on 'aFormat' with a variadic number of arbitrarily typed arguments.
*
* @param aFormat
* @param aArguments
* @return
*/
template <typename... TArgs>
static std::string format(
std::string const&aFormat,
TArgs &&...aArguments);
/**
* Accept an arbitrarily typed argument and convert it to it's proper
* string representation.
*
* @tparam TArg
* @tparam TEnable
* @param aArg
* @return
*/
template <
typename TArg,
typename TEnable = void
>
static std::string toString(TArg const &aArg);
/**
* Accept a float argument and convert it to it's proper string representation.
*
* @tparam TArg
* @param arg
* @return
*/
template <
typename TArg,
typename std::enable_if<std::is_floating_point<TArg>::value, TArg>::type
>
static std::string toString(const float& arg);
/**
* Convert a string into an arbitrarily typed representation.
*
* @param aString
* @return
*/
template <
typename TData,
typename TEnable = void
>
static TData const fromString(std::string const &aString);
template <
typename TData,
typename std::enable_if
<
std::is_integral<TData>::value || std::is_floating_point<TData>::value,
TData
>::type
>
static TData fromString(std::string const &aString);
private:
/**
* Format a list of arguments. In this case zero arguments as the abort-condition
* of the recursive expansion of the parameter pack.
*
* @param aArguments
*/
template <std::size_t NArgs>
static void formatArguments(std::array<std::string, NArgs> const &aArguments);
/**
* Format a list of arguments of arbitrary type and expand recursively.
*
* @param outFormatted
* @param inArg
* @param inArgs
*/
template <
std::size_t NArgs,
typename TArg,
typename... TArgs
>
static void formatArguments(
std::array<std::string, NArgs> &aOutFormatted,
TArg &&aInArg,
TArgs &&...aInArgs);
};
//<-----------------------------------------------------------------------------
//<-----------------------------------------------------------------------------
//<
//<-----------------------------------------------------------------------------
template <typename... TArgs>
std::string CString::format(
const std::string &aFormat,
TArgs &&...aArgs)
{
std::array<std::string, sizeof...(aArgs)> formattedArguments{};
formatArguments(formattedArguments, std::forward<TArgs>(aArgs)...);
if constexpr (sizeof...(aArgs) == 0)
{
return aFormat;
}
else {
uint32_t number = 0;
bool readNumber = false;
std::ostringstream stream;
for(std::size_t k = 0; k < aFormat.size(); ++k)
{
switch(aFormat[k])
{
case '%':
readNumber = true;
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
// Desired behaviour to enable reading numbers in text w/o preceding %
#pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
if(readNumber)
{
number *= 10;
number += static_cast<uint32_t>(aFormat[k] - '0');
break;
}
default:
if(readNumber)
{
stream << formattedArguments[std::size_t(number)];
readNumber = false;
number = 0;
}
stream << aFormat[k];
break;
#pragma GCC diagnostic warning "-Wimplicit-fallthrough"
}
}
if(readNumber)
{
stream << formattedArguments[std::size_t(number)];
readNumber = false;
number = 0;
}
return stream.str();
}
}
//<-----------------------------------------------------------------------------
//<-----------------------------------------------------------------------------
//<
//<-----------------------------------------------------------------------------
template <typename TArg, typename enable>
std::string CString::toString(TArg const &aArg)
{
std::ostringstream stream;
stream << aArg;
return stream.str();
}
//<-----------------------------------------------------------------------------
//<-----------------------------------------------------------------------------
//<
//<-----------------------------------------------------------------------------
template <
typename TArg,
typename std::enable_if<std::is_floating_point<TArg>::value, TArg>::type
>
std::string CString::toString(const float& arg) {
std::ostringstream stream;
stream << std::setprecision(12) << arg;
return stream.str();
}
//<-----------------------------------------------------------------------------
//<-----------------------------------------------------------------------------
//<
//<-----------------------------------------------------------------------------
template <std::size_t argCount>
void CString::formatArguments(std::array<std::string, argCount> const&aArgs)
{
// Unused: aArgs
}
//<-----------------------------------------------------------------------------
//<-----------------------------------------------------------------------------
//<
//<-----------------------------------------------------------------------------
template <std::size_t argCount, typename TArg, typename... TArgs>
void CString::formatArguments(
std::array<std::string, argCount> &outFormatted,
TArg &&inArg,
TArgs &&...inArgs)
{
// Executed for each, recursively until there's no param left.
uint32_t const index = (argCount - 1 - sizeof...(TArgs));
outFormatted[index] = toString(inArg);
formatArguments(outFormatted, std::forward<TArgs>(inArgs)...);
}
//<-----------------------------------------------------------------------------
//<-----------------------------------------------------------------------------
//<
//<-----------------------------------------------------------------------------
template <
typename TData,
typename std::enable_if
<
std::is_integral<TData>::value || std::is_floating_point<TData>::value,
TData
>::type
>
TData CString::fromString(std::string const &aString)
{
TData const result{};
std::stringstream ss(aString);
ss >> result;
return result;
}
//<-----------------------------------------------------------------------------
}