C++中的"字符串插值":构造一个带有嵌入值的std :: string(例如,用于错误消息)?

sto*_*tic 24 c++ c++11

我想创建一个包含嵌入信息的字符串.实现我想要的一种方式(不是唯一的方法)称为字符串插值或变量替换,其中字符串中的占位符用实际值替换.

在C中,我会做这样的事情:

printf("error! value was %d but I expected %d",actualValue,expectedValue)
Run Code Online (Sandbox Code Playgroud)

而如果我在python中编程,我会做这样的事情:

"error! value was {0} but I expected {1}".format(actualValue,expectedValue)
Run Code Online (Sandbox Code Playgroud)

这两个都是字符串插值的例子.

我怎么能用C++做到这一点?

重要提示:

  1. 我知道我可以使用,std::cout如果我想将这样的消息打印到标准输出(不是字符串插值,但打印出我想要的字符串类型):
cout << "error! value was " << actualValue << " but I expected "
<< expectedValue;
Run Code Online (Sandbox Code Playgroud)

我不想将字符串打印到stdout.我想将std::string一个参数作为参数传递给一个函数(例如异常对象的构造函数).

  1. 我正在使用C++ 11,但可移植性可能是一个问题,所以知道哪些方法有效,哪些方法不适用于哪个版本的C++.

编辑

  1. 对于我的直接使用,我并不关心性能(我正在大声提出异常!). 但是,了解各种方法的相对性能通常非常有用.

  2. 为什么不直接使用printf(毕竟C++是C的超集......)? 这个答案讨论了一些原因.根据我的理解,类型安全是一个重要原因:如果你把%d放在那里,你放在那里的变量最好真的可以转换为整数,因为这就是函数如何计算它是什么类型.使用一种方法可以更加安全,该方法使用要插入的变量的实际类型的编译时知识.

inf*_*inf 36

在 C++20 中,您将能够使用std::format.

这将支持 python 样式格式:

string s = std::format("{1} to {0}", "a", "b");
Run Code Online (Sandbox Code Playgroud)

已经有一个可用的实现:https : //github.com/fmtlib/fmt

  • 另请参阅 cppreference.com 上的 [`std::format()`](https://en.cppreference.com/w/cpp/utility/format/format)。请注意,在格式占位符中指定索引是可选的:`string s1 = std::format("{} to {}", "a", "b");` (3认同)
  • 这远不如 Python f 字符串格式那么好,后者只是 `f"{a} 到 {b}"` (3认同)

sto*_*tic 16

方法1:使用字符串流

它看起来像std::stringstream一个快速的解决方案:

std::stringstream ss;
ss << "error! value was " << actualValue << " but I expected " <<  expectedValue << endl;

//example usage
throw MyException(ss.str())
Run Code Online (Sandbox Code Playgroud)

  • 没有外部依赖
  • 我相信这适用于C++ 03以及c ++ 11.

  • 据说很慢
  • 更麻烦:你必须创建一个流,写入它,然后从中获取字符串.

方法2:提升格式

升压格式库也是一种可能性.使用这个,你会做:

throw MyException(boost::format("error! value was %1% but I expected %2%") % actualValue % expectedValue);
Run Code Online (Sandbox Code Playgroud)

  • 与stringstream方法相比非常干净:一个紧凑的构造

  • 据报道很慢:在内部使用stream方法
  • 这是外部依赖

编辑:

方法3:可变参数模板参数

似乎可以通过使用可变参数模板参数(对于采用无限数量模板参数的模板的技术术语)来创建类型安全的printf版本.我已经看到了这方面的一些可能性:

  • 这个问题提供了一个紧凑的例子,并讨论了该示例的性能问题.
  • 这个问题的答案,其实施也相当紧凑,但据报道仍然存在性能问题.
  • 据报道,在这个答案中讨论的fmt库非常快,似乎和printf本身一样干净,但是它是一个外部依赖

  • 用法很简洁:只需调用类似printf的函数
  • 据报道,fmt库非常快
  • 其他选项看起来非常紧凑(不需要外部依赖)

  • fmt库虽然速度很快,但却是一种外部依赖
  • 其他选择显然有一些性能问题

  • 值得注意的是,第一个解决方案将很难在以后进行本地化.有意义的消息溢出到没有意义的部分,并且一些语言可能需要不同的参数顺序来形成声音消息 (5认同)
  • @stochastic"某些语言",如真实语言,而不是编程语言. (4认同)
  • 是的,它最好将整个字符串作为一个整体进行本地化.使用`boost :: format()`的另一种方法是简单的[`std :: string :: replace()`](http://en.cppreference.com/w/cpp/string/basic_string/replace)填写占位符,例如:`std :: string s ="error!value是%1%但我预计%2%"; std :: string :: size_type idx = s.find("%1%"); s.replace(idx,3,std :: to_string(acutalValue)); idx = s.find("%2%"); s.replace(idx,3,std :: to_string(expectedValue)); 抛出MyException(s);`不像`boost :: format()`,甚至`std :: stringstream`那样优雅,但在简单的情况下仍然可用. (2认同)

Pet*_*eng 7

在C++ 11中,您可以使用std::to_string:

"error! value was " + std::to_string(actualValue) + " but I expected " + std::to_string(expectedValue)
Run Code Online (Sandbox Code Playgroud)

它不漂亮,但它很简单,你可以使用宏来缩小它.性能不是很好,因为你reserve()事先没有空间.Variadic模板可能更快,看起来更好.

这种字符串结构(而不​​是插值)对于本地化也是不好的,但如果需要,你可能会使用库.


Vya*_*sky 5

使用任何你喜欢的:

1) std::stringstream

#include <sstream>
std::stringstream ss;
ss << "Hello world!" << std::endl;
throw std::runtime_error(ss.str());
Run Code Online (Sandbox Code Playgroud)

2)libfmt:https : //github.com/fmtlib/fmt

#include <stdexcept>
throw std::runtime_error(
    fmt::format("Error has been detected with code {} while {}",
        0x42, "copying"));
Run Code Online (Sandbox Code Playgroud)