C++中的快速格式化字符串

18

在构造std::string时,是否有一种快速简便的方法来获得类似于sprintf的格式化功能?就像这样...

std::string foo("A number (%d) and a character (%c).\n", 18, 'a');

看看我的解决方案。这是C++风格的解决方案。不像boost那样依赖于涉及%d和%c的C风格格式字符串。 - Nawaz
4个回答

16

虽然字符串的构造函数中不包含此功能,但你可能希望查看boost format。它是一种更安全的sprintf样式的字符串格式化工具。通过它,至少可以实现以下内容:

std::string foo = 
    boost::str(boost::format("A number (%d) and a character (%c)\n") % 18 % 'a');

18 字面量替换为 uint8_t,然后,你的 Boost 输出就会完全崩溃:你将收到两个字符而不是带有数字的字符。 - Anton Samsonov

11
我写了一个stringbuilder类,你可以使用它,我认为它比boost::format更好,因为与boost::format不同,stringbuilder不使用C风格格式化字符串,如%d、%c等。以下是如何在一行代码中使用stringbuilder:
std::string s=stringbuilder() << "A number " << 18 <<" and a character " <<'a';
< p > stringbuilder 的实现非常简单:

struct stringbuilder
{
   std::stringstream ss;
   template<typename T>
   stringbuilder & operator << (const T &data)
   {
        ss << data;
        return *this;
   }
   operator std::string() { return ss.str(); }
};

在ideone上有演示:http://ideone.com/J9ALB


我刚写了一篇博客,描述了使用stringbuilder的许多不同方式。


7
谢谢分享 :) 但是我更喜欢C风格的格式化语法。我个人认为 "这是我的字符串,替换相应的令牌" 的格式更加舒适,而不是一块一块地拼装它们。 - salezica
4
boost::format是专门开发的格式化工具,其格式化和printf类似,并且与iostream一样安全。 - Benjamin Lindley
3
@Nawaz: 一旦你编写了国际化软件,你就会喜欢使用C格式化风格(当然要使用安全的方法),因为它可以在最终翻译中移动动态位。IOStream 支持facet,但它们会让你付出代价,并且并没有帮助到国际化。:/ - Matthieu M.
5
假设你想让用户选择一个形容词。在法语中,你可以使用句子printf("J'ai vu des oiseaux %s", adjective);通过流来实现:os << "J'ai vu des oiseaux " << adjective。然而在英语中,os << "I have seen birds " << adjective是不正确的。你需要使用printf("I have seen %s birds", adjective)来表示。这个句子的分割方式完全不同。在这种情况下,你需要使用 C 风格格式化而不是流。(或者你需要为每个语言更改流,但这很繁琐,请查看 gettext 如何处理资源)。 - Matthieu M.
@Matthieu:很棒的例子。非常感谢。顺便问一下,我怎么知道呢?我不会法语 :P - Nawaz
显示剩余10条评论

7

fmt库提供了一种安全的sprintf实现,可以返回字符串:

std::string foo = fmt::sprintf("A number (%d) and a character (%c).\n", 18, 'a');

这个库也支持Python的 str.format 语法。
std::string foo = fmt::format("A number ({}) and a character ({}).\n", 18, 'a');

这个库的目的类似于Boost Format,但是它速度更快,体积较小,并且除了标准C ++库之外没有其他外部依赖。

免责声明:我是这个库的作者。


1

这个怎么样,它直接使用printf:

#include <stdio.h> 
#include <stdarg.h> 

std::string format(const char *fmt, ...) 
{ 
    va_list ap; 
    va_start(ap, fmt); 

    const size_t SIZE = 512; 
    char buffer[SIZE] = { 0 }; 
    vsnprintf(buffer, SIZE, fmt, ap); 

    va_end(ap); 

    return std::string(buffer); 
} 

最好查看vsnprintf的返回值以分配正确大小的缓冲区,而不是将其截断为512个字符。或者在Linux上使用asprintf。此外,使用gcc风格的函数属性,以便在参数上仍然获得一些类型安全性。 - poolie

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