C++11等效于Boost.Format

25

在C++11标准中有像Boost.Format这样的东西吗?对于我所遇到的所有其他需求,我都能够避免使用Boost并选择更好的C++11选项。

说到这一点,Boost.Format与Python的format()语法相比不能相提并论。类似于这样的东西将更加优秀。


好用的C/K&R xxprintf()有什么问题吗? - FoggyDay
7
总体而言我喜欢它,但它不能直接接受字符串,这很烦人。我希望有一种方法不需要在所有字符串上调用.c_str()。另外,它远不如Python的 format() 好用。 - Chris Redford
4
没有类型安全,完全没有扩展性。 - Lightness Races in Orbit
5个回答

32
C++11、14和17没有提供类似的功能。然而,C++20提供了std::format,它在精神上类似于Boost Format,但设计允许更有效的实现。{fmt}库是这种格式化工具的实现,它只需要C++11。
std::string s = fmt::format("I'd rather be {1} than {0}.", "right", "happy");

免责声明:我是{fmt}和C++20 std::format的作者。


7

2
使用c++11正则表达式和可变参数模板实现类似Python的格式化字符串函数。
/**
   Helper code to unpack variadic arguments
*/
namespace internal
{
    template<typename T>
    void unpack(std::vector<std::string> &vbuf, T t)
    {
        std::stringstream buf;
        buf << t;
        vbuf.push_back(buf.str());
    }
    template<typename T, typename ...Args>
    void unpack(std::vector<std::string> &vbuf, T t, Args &&... args)
    {
        std::stringstream buf;
        buf << t;
        vbuf.push_back(buf.str());
        unpack(vbuf, std::forward<Args>(args)...);
    }
}

/**
    Python-like string formatting
 */
template<typename ... Args>
std::string format(const std::string& fmt, Args ... args)
{
    std::vector<std::string> vbuf;  // store arguments as strings
    std::string in(fmt), out;    // unformatted and formatted strings
    std::regex re_arg("\\{\\b\\d+\\b\\}");  // search for {0}, {1}, ...
    std::regex re_idx("\\b\\d+\\b");        // search for 0, 1, ...
    std::smatch m_arg, m_idx;               // store matches
    size_t idx = 0;                         // index of argument inside {...}

    // Unpack arguments and store them in vbuf
    internal::unpack(vbuf, std::forward<Args>(args)...);

    // Replace all {x} with vbuf[x]
    while (std::regex_search(in, m_arg, re_arg)) {
        out += m_arg.prefix();
        auto text = m_arg[0].str();
        if (std::regex_search(text, m_idx, re_idx)) {
            idx = std::stoi(m_idx[0].str());
        }
        if(idx < vbuf.size()) {
            out += std::regex_replace(m_arg[0].str(), re_arg, vbuf[idx]);
        }
        in = m_arg.suffix();
    }
    out += in;
    return out;
}

示例:cpp.sh/9cvtz

当我使用C++11通过头文件包含模板并调用格式时,会出现“使用已删除的函数”错误。你能帮我解决一下吗? - Markus Dutschke
当然,您能提供一个重现错误的代码片段吗? - galarius
明天我会在Stack Overflow上提出一个问题,并附上链接。但无论如何,要产生这个错误,只需将这两个模板放在头文件中并在主函数中包含它即可。这应该足够了。不过我明天会仔细检查一下。 - Markus Dutschke
好的,当在一个最小化的例子上工作时,结果发现代码甚至无法在其他在线编译器或g++中编译。这是一个例子:https://onlinegdb.com/rJNMvHDNv - Markus Dutschke
我明白了,regex_search不再允许使用临时字符串,所以在调用regex_search之前添加这个:auto tmp = m_arg[0].str();并将tmp作为第一个参数传递。 - galarius

0
template<typename... Args>
std::string fmt_str(const std::string& fmt, Args... args)
{
    static const int bufferSize = 1000;
    char buffer[bufferSize];
    int n = snprintf(buffer, bufferSize, fmt.c_str(), args...);
    assert(n >= 0 and n <= bufferSize - 1 && "check fmt_str output");

    return (buffer);
}

//基于Markus的一些小改进:将输入更改为引用,避免缓冲区新建,使用snprintf避免缓冲区溢出,直接返回以避免复制构造函数。在我的项目中使用它

//这应该是对Markus Dutschke的评论,而不是答案,但是评论字段无法很好地格式化代码片段,因此我在此提取了代码。


0

使用sprintf

  • 对于C++20,使用std::format
  • 从C++11版本开始有fmt-library
  • 一些简单的格式化输出解决方案是printf
  • 要使用printf语法来编写std::string,请使用以下代码片段

最小可复现示例:使用printf语法格式化std::string

交互式版本

#include <iostream>
#include <string>
#include <stdio.h>
#include <assert.h>


template<typename... Args>
std::string fmt_str(std::string fmt, Args... args)
{
    size_t bufferSize = 1000;
    char *buffer = new char[bufferSize];
    int n = sprintf(buffer, fmt.c_str(), args...);
    assert (n >= 0 and n < (int) bufferSize - 1  && "check fmt_str output");

    std::string fmtStr (buffer);
    delete buffer;
    return fmtStr;
}



int main()
{
    int a=1, b=2;
    double c=3.;
    std::cout << fmt_str("%d plus %d is %f", a, b, c) << std::endl;
    return 0;
}

输出

1 plus 2 is 3.000000

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接