std :: string格式,如sprintf

我必须使用sprintf格式化std::string并将其发送到文件stream中。 我该怎么做?

你不能直接这样做,因为你没有对底层缓冲区的写访问权限(直到C ++ 11;参见Dietrich Epp的评论 )。 你必须首先在一个cstring中,然后将它复制到一个std :: string:

  char buff[100]; snprintf(buff, sizeof(buff), "%s", "Hello"); std::string buffAsStdStr = buff; 

但我不知道为什么你不会只使用stringstream? 我假设你有具体的理由不只是这样做:

  std::ostringstream stringStream; stringStream << "Hello"; std::string copyOfStr = stringStream.str(); 
 #include <stdarg.h> // For va_start, etc. std::string string_format(const std::string fmt, ...) { int size = ((int)fmt.size()) * 2 + 50; // Use a rubric appropriate for your code std::string str; va_list ap; while (1) { // Maximum two passes on a POSIX system... str.resize(size); va_start(ap, fmt); int n = vsnprintf((char *)str.data(), size, fmt.c_str(), ap); va_end(ap); if (n > -1 && n < size) { // Everything worked str.resize(n); return str; } if (n > -1) // Needed size returned size = n + 1; // For null char else size *= 2; // Guess at a larger size (OS specific) } return str; } 

更安全,更高效(我testing了它,速度更快)方法:

 #include <stdarg.h> // For va_start, etc. #include <memory> // For std::unique_ptr std::string string_format(const std::string fmt_str, ...) { int final_n, n = ((int)fmt_str.size()) * 2; /* Reserve two times as much as the length of the fmt_str */ std::unique_ptr<char[]> formatted; va_list ap; while(1) { formatted.reset(new char[n]); /* Wrap the plain char array into the unique_ptr */ strcpy(&formatted[0], fmt_str.c_str()); va_start(ap, fmt_str); final_n = vsnprintf(&formatted[0], n, fmt_str.c_str(), ap); va_end(ap); if (final_n < 0 || final_n >= n) n += abs(final_n - n + 1); else break; } return std::string(formatted.get()); } 

fmt_str是按值传递的,以符合va_start的要求。

注:“更安全”和“更快”版本在某些系统上不起作用。 因此两者仍然列出。 此外,“更快”完全取决于预分配步骤是否正确,否则strcpy会使其更慢。

使用C ++ 11 std::snprintf ,这变成了一个非常简单和安全的任务。 我看到很多关于这个问题的答案,这些答案显然是在使用固定缓冲区长度和vargs的C ++ 11之前编写的,为了安全,有效和清晰的原因,我不build议这样做。

 #include <memory> #include <iostream> #include <string> #include <cstdio> using namespace std; //Don't if you're in a header-file template<typename ... Args> string string_format( const std::string& format, Args ... args ) { size_t size = snprintf( nullptr, 0, format.c_str(), args ... ) + 1; // Extra space for '\0' unique_ptr<char[]> buf( new char[ size ] ); snprintf( buf.get(), size, format.c_str(), args ... ); return string( buf.get(), buf.get() + size - 1 ); // We don't want the '\0' inside } 

上面的代码片段在CC0 1.0下获得许可。

逐行解释:

目标:使用std::snprintf写入char* ,然后将其转换为std::string

首先,我们确定char数组的期望长度。

来自cppreference.com :

返回值

[…]如果由于buf_size限制而导致结果string被截断,则函数将返回如果没有施加该限制,则会写入的字符总数(不包括终止的空字节)。

这意味着所需的大小是字符数加1 ,这样空终止符就会位于所有其他字符之后,并且可以被string构造函数再次截断。 这个问题在评论中由@ alexk7解释。

然后,我们分配一个新的字符数组,并将其分配给一个std::unique_ptr 。 一般build议这样做,因为您不必再​​次手动delete它。

请注意,这是不是一个安全的方式来分配一个unique_ptr与用户定义的types,因为如果构造函数抛出一个exception,你不能释放内存!

之后,我们当然可以使用snprintf作为它的用途,并将格式化的string写入char[] ,然后创build并返回一个新的std::string


你可以在这里看到一个例子。


Visual Studio用户的附加信息:

正如在这个答案中所解释的那样,Microsoft将std::snprintf重命名为_snprintf (是的,没有使用std:: _snprintf 。 MS进一步将其设置为不build议使用,并build议使用_snprintf_s ,但是_snprintf_s不会接受缓冲区为零或小于格式化的输出,并且如果发生这种情况将不计算输出长度。 因此,为了摆脱编译过程中的弃用警告,可以在包含_snprintf的文件顶部插入以下行 :

 #pragma warning(disable : 4996) 

boost::format()提供了你想要的function:

从Boost格式的图书馆大纲:

格式对象是由格式string构造的,然后通过重复调用运算符%给出参数。 然后将这些参数中的每一个转换为string,根据格式string将这些string合并为一个string。

 cout << boost::format("writing %1%, x=%2% : %3%-th try") % "toto" % 40.23 % 50; // prints "writing toto, x=40.230 : 50-th try" 

不幸的是,这里的大多数答案都使用可变参数,这些参数本质上是不安全的,除非你使用像GCC format属性那样只能用于字面格式string的东西。 你可以看到为什么这些函数在下面的例子中是不安全的:

 std::string format_str = "%s"; string_format(format_str, format_str[0]); 

string_format是Erik Aronesty的答案。 这段代码可以编译,但是当你尝试运行时,它很可能会崩溃:

 $ g++ -Wall -Wextra -pedantic test.cc $ ./a.out Segmentation fault: 11 

可以实现一个安全的printf并使用(variadic)模板将其扩展为格式std::string 。 这已经在fmt库中完成了,它提供了一个替代sprintf返回std::string的安全选项:

 std::string format_str = "The answer is %d"; std::string result = fmt::sprintf(format_str, 42); 

fmt跟踪参数types,如果types不符合格式规范,则不存在分段错误,只是一个例外。

免责声明 :我是这个图书馆的作者。

如果你只想要一个类似于printf的语法(不需要自己调用printf),可以看看Boost格式 。

我用vsnprintf编写了自己的代码,所以它返回string,而不必创build自己的缓冲区。

 #include <string> #include <cstdarg> //missing string printf //this is safe and convenient but not exactly efficient inline std::string format(const char* fmt, ...){ int size = 512; char* buffer = 0; buffer = new char[size]; va_list vl; va_start(vl, fmt); int nsize = vsnprintf(buffer, size, fmt, vl); if(size<=nsize){ //fail delete buffer and try again delete[] buffer; buffer = 0; buffer = new char[nsize+1]; //+1 for /0 nsize = vsnprintf(buffer, size, fmt, vl); } std::string ret(buffer); va_end(vl); delete[] buffer; return ret; } 

所以你可以使用它

 std::string mystr = format("%s %d %10.5f", "omg", 1, 10.5); 

添加一个可变模板版本'vtspf(..)':编辑'17 / 8/31'

 template<typename T> const std::string type_to_string(const T &v) { std::ostringstream ss; ss << v; return ss.str(); }; template<typename T> const T string_to_type(const std::string &str) { std::istringstream ss(str); T ret; ss >> ret; return ret; }; template<typename...P> void vtspf_priv(std::string &s) {} template<typename H, typename...P> void vtspf_priv(std::string &s, H h, P...p) { s+=type_to_string(h); vtspf_priv(s, p...); } template<typename...P> std::string temp_vtspf(P...p) { std::string s(""); vtspf_priv(s, p...); return s; } 

这实际上是一个逗号分隔的版本(而不是)有时候会妨碍<< -operators,这样使用:

 char chSpace=' '; double pi=3.1415; std::string sWorld="World", str_var; str_var = vtspf("Hello", ',', chSpace, sWorld, ", pi=", pi); 

适应使用在Erik Aronesty的答复(上面)的技术:

 #include <string> #include <cstdarg> #include <cstdio> //============================================================================= void spf(std::string &s, const std::string fmt, ...) { int n, size=100; bool b=false; va_list marker; while (!b) { s.resize(size); va_start(marker, fmt); n = vsnprintf((char*)s.c_str(), size, fmt.c_str(), marker); va_end(marker); if ((n>0) && ((b=(n<size))==true)) s.resize(n); else size*=2; } } //============================================================================= void spfa(std::string &s, const std::string fmt, ...) { std::string ss; int n, size=100; bool b=false; va_list marker; while (!b) { ss.resize(size); va_start(marker, fmt); n = vsnprintf((char*)ss.c_str(), size, fmt.c_str(), marker); va_end(marker); if ((n>0) && ((b=(n<size))==true)) ss.resize(n); else size*=2; } s += ss; } 

[上一个回答]
一个很晚的答案,但是对于像我这样的人来说,我喜欢“sprintf”方式:我已经写和正在使用下面的函数。 如果你喜欢它,你可以展开%-options来更贴近sprintf的; 那里的那些已经足够满足我的需要了。 你可以像使用sprintf一样使用stringf()和stringfappend()。 请记住,参数…必须是PODtypes。

 //============================================================================= void DoFormatting(std::string& sF, const char* sformat, va_list marker) { char *s, ch=0; int n, i=0, m; long l; double d; std::string sf = sformat; std::stringstream ss; m = sf.length(); while (i<m) { ch = sf.at(i); if (ch == '%') { i++; if (i<m) { ch = sf.at(i); switch(ch) { case 's': { s = va_arg(marker, char*); ss << s; } break; case 'c': { n = va_arg(marker, int); ss << (char)n; } break; case 'd': { n = va_arg(marker, int); ss << (int)n; } break; case 'l': { l = va_arg(marker, long); ss << (long)l; } break; case 'f': { d = va_arg(marker, double); ss << (float)d; } break; case 'e': { d = va_arg(marker, double); ss << (double)d; } break; case 'X': case 'x': { if (++i<m) { ss << std::hex << std::setiosflags (std::ios_base::showbase); if (ch == 'X') ss << std::setiosflags (std::ios_base::uppercase); char ch2 = sf.at(i); if (ch2 == 'c') { n = va_arg(marker, int); ss << std::hex << (char)n; } else if (ch2 == 'd') { n = va_arg(marker, int); ss << std::hex << (int)n; } else if (ch2 == 'l') { l = va_arg(marker, long); ss << std::hex << (long)l; } else ss << '%' << ch << ch2; ss << std::resetiosflags (std::ios_base::showbase | std::ios_base::uppercase) << std::dec; } } break; case '%': { ss << '%'; } break; default: { ss << "%" << ch; //i = m; //get out of loop } } } } else ss << ch; i++; } va_end(marker); sF = ss.str(); } //============================================================================= void stringf(string& stgt,const char *sformat, ... ) { va_list marker; va_start(marker, sformat); DoFormatting(stgt, sformat, marker); } //============================================================================= void stringfappend(string& stgt,const char *sformat, ... ) { string sF = ""; va_list marker; va_start(marker, sformat); DoFormatting(sF, sformat, marker); stgt += sF; } 

为了以'sprintf'的方式格式化std::string ,调用snprintf (参数nullptr0 )来获得需要的缓冲区的长度。 用C ++ 11 variadic模板编写你的函数,像这样:

 #include <cstdio> #include <string> #include <cassert> template< typename... Args > std::string string_sprintf( const char* format, Args... args ) { int length = std::snprintf( nullptr, 0, format, args... ); assert( length >= 0 ); char* buf = new char[length + 1]; std::snprintf( buf, length + 1, format, args... ); std::string str( buf ); delete[] buf; return std::move(str); } 

用C ++ 11编译支持,例如在GCC中: g++ -std=c++11

用法:

  std::cout << string_sprintf("%g, %g\n", 1.23, 0.001); 

这是谷歌如何做到这一点: StringPrintf (BSD许可证)
和Facebook以非常相似的方式: StringPrintf (Apache许可证)
两者都提供了一个方便的StringAppendF

我在这个非常受欢迎的问题上有两分钱。

引用printf类函数的manpage :

成功返回后,这些函数返回打印的字符数(不包括用于结束输出的空字节)。

函数snprintf()和vsnprintf()不会写入多于大小的字节(包括终止空字节('\ 0'))。 如果输出由于这个限制而被截断,那么返回值就是如果有足够的空间可用的话,将被写入最终string的字符数(不包括终止空字节)。 因此,大小或更大的返回值意味着输出被截断。

换句话说,一个理智的C ++ 11实现应该如下:

 #include <string> #include <cstdio> template <typename... Ts> std::string fmt (const std::string &fmt, Ts... vs) { char b; unsigned required = std::snprintf(&b, 0, fmt.c_str(), vs...) + 1; // See comments: the +1 is necessary, while the first parameter // can also be set to nullptr char bytes[required]; std::snprintf(bytes, required, fmt.c_str(), vs...); return std::string(bytes); } 

它工作得很好:)

variables模板仅在C ++ 11中受支持。 像素点的答案显示了使用较旧的编程风格的类似技术。

奇怪的是,C ++没有这样的开箱即用的东西。 他们最近添加了to_string() ,这在我看来是一个很大的进步。 我想知道如果他们最终将添加一个.format运算符到std::string

编辑

正如alexk7指出的那样, std::snprintf的返回值需要+1 ,因为我们需要为\0字节留出空间。 直观地说,在大多数架构中,缺less+1将导致required整数部分被0覆盖。 在评估std::snprintf实际参数之后会发生这种情况,所以效果不应该是可见的。

然而,这个问题可能会改变,例如编译器优化:如果编译器决定使用一个寄存器为requiredvariables呢? 这是有时会导致安全问题的那种错误。

string没有你所需要的,但是std :: stringstream呢。 使用stringstream创buildstring,然后提取string。 这里是你可以做的事情的全面清单。 例如:

 cout.setprecision(10); //stringstream is a stream like cout 

打印双精度或浮点数时会给你十位精度。

基于Erik Aronesty提供的答案:

 std::string string_format(const std::string &fmt, ...) { std::vector<char> str(100,'\0'); va_list ap; while (1) { va_start(ap, fmt); auto n = vsnprintf(str.data(), str.size(), fmt.c_str(), ap); va_end(ap); if ((n > -1) && (size_t(n) < str.size())) { return str.data(); } if (n > -1) str.resize( n + 1 ); else str.resize( str.size() * 2); } return str.data(); } 

这就避免了需要从原始答案中的.c_str()的结果中.c_str() const

 inline void format(string& a_string, const char* fmt, ...) { va_list vl; va_start(vl, fmt); int size = _vscprintf( fmt, vl ); a_string.resize( ++size ); vsnprintf_s((char*)a_string.data(), size, _TRUNCATE, fmt, vl); va_end(vl); } 
 template<typename... Args> std::string string_format(const char* fmt, Args... args) { size_t size = snprintf(nullptr, 0, fmt, args...); std::string buf; buf.reserve(size + 1); buf.resize(size); snprintf(&buf[0], size + 1, fmt, args...); return buf; } 

使用C99 snprintf和C ++ 11

如果你在一个有asprintf(3)的系统上,你可以很容易地包装它:

 #include <iostream> #include <cstdarg> #include <cstdio> std::string format(const char *fmt, ...) __attribute__ ((format (printf, 1, 2))); std::string format(const char *fmt, ...) { std::string result; va_list ap; va_start(ap, fmt); char *tmp = 0; int res = vasprintf(&tmp, fmt, ap); va_end(ap); if (res != -1) { result = tmp; free(tmp); } else { // The vasprintf call failed, either do nothing and // fall through (will return empty string) or // throw an exception, if your code uses those } return result; } int main(int argc, char *argv[]) { std::string username = "you"; std::cout << format("Hello %s! %d", username.c_str(), 123) << std::endl; return 0; } 

你可以试试这个:

 string str; str.resize( _MAX_PATH ); sprintf( &str[0], "%s %s", "hello", "world" ); // optionals // sprintf_s( &str[0], str.length(), "%s %s", "hello", "world" ); // Microsoft // #include <stdio.h> // snprintf( &str[0], str.length(), "%s %s", "hello", "world" ); // c++11 str.resize( strlen( str.data() ) + 1 ); 

这是我用来在我的程序中执行此操作的代码…这不是什么幻想,但它的确有窍门…请注意,您将不得不根据情况调整您的大小。 MAX_BUFFER对我来说是1024。

 std::string Format ( const char *fmt, ... ) { char textString[MAX_BUFFER*5] = {'\0'}; // -- Empty the buffer properly to ensure no leaks. memset(textString, '\0', sizeof(textString)); va_list args; va_start ( args, fmt ); vsnprintf ( textString, MAX_BUFFER*5, fmt, args ); va_end ( args ); std::string retStr = textString; return retStr; } 

从达卡夫和像素点的答案 。 我玩了一下,得到这个:

 #include <cstdarg> #include <cstdio> #include <string> std::string format(const char* fmt, ...) { va_list vl; va_start(vl, fmt); int size = vsnprintf(0, 0, fmt, vl) + sizeof('\0'); va_end(vl); char buffer[size]; va_start(vl, fmt); size = vsnprintf(buffer, size, fmt, vl); va_end(vl); return std::string(buffer, size); } 

有了健全的编程习惯,我相信代码应该足够了,但是我仍然对更加安全的替代品开放,这些替代品仍然很简单,不需要C ++ 11。


这里有另一个版本,当初始缓冲区已经足够的时候,使用初始缓冲区来防止再次调用vsnprintf()

 std::string format(const char* fmt, ...) { va_list vl; int size; enum { INITIAL_BUFFER_SIZE = 512 }; { char buffer[INITIAL_BUFFER_SIZE]; va_start(vl, fmt); size = vsnprintf(buffer, INITIAL_BUFFER_SIZE, fmt, vl); va_end(vl); if (size < INITIAL_BUFFER_SIZE) return std::string(buffer, size); } size += sizeof('\0'); char buffer[size]; va_start(vl, fmt); size = vsnprintf(buffer, size, fmt, vl); va_end(vl); return std::string(buffer, size); } 

(事实certificate,这个版本与Piti Ongmongkolkul的答案类似,只是它不使用newdelete[] ,而且在创buildstd::string时指定了一个大小。

这里不使用newdelete[]的想法是暗示在堆上使用堆栈,因为它不需要调用分配和释放函数,但是如果没有正确使用,可能会有一些危险的缓冲区溢出也许是旧的,也许只是脆弱的)系统。 如果这是一个问题,我强烈build议使用newdelete[]来代替。 请注意,这里唯一关心的是分配,因为vsnprintf()已经被调用了限制,所以根据第二个缓冲区上分配的大小来指定一个限制也会阻止这些。)

非常非常简单的解决scheme。

 std::string strBuf; strBuf.resize(256); int iCharsPrinted = sprintf_s((char *)strPath.c_str(), strPath.size(), ...); strBuf.resize(iCharsPrinted); 

我通常使用这个:

 std::string myformat(const char *const fmt, ...) { char *buffer = NULL; va_list ap; va_start(ap, fmt); (void)vasprintf(&buffer, fmt, ap); va_end(ap); std::string result = buffer; free(buffer); return result; } 

缺点:并不是所有的系统都支持vasprint

下面稍微修改@iFreilicht的答案,更新到C ++ 14 (使用make_unique函数代替原始声明),并添加了对std::string参数的支持(基于Kenny Kerr的文章 )

 #include <iostream> #include <memory> #include <string> #include <cstdio> template <typename T> T process_arg(T value) noexcept { return value; } template <typename T> T const * process_arg(std::basic_string<T> const & value) noexcept { return value.c_str(); } template<typename ... Args> std::string string_format(const std::string& format, Args const & ... args) { const auto fmt = format.c_str(); const size_t size = std::snprintf(nullptr, 0, fmt, process_arg(args) ...) + 1; auto buf = std::make_unique<char[]>(size); std::snprintf(buf.get(), size, fmt, process_arg(args) ...); auto res = std::string(buf.get(), buf.get() + size - 1); return res; } int main() { int i = 3; float f = 5.f; char* s0 = "hello"; std::string s1 = "world"; std::cout << string_format("i=%d, f=%f, s=%s %s", i, f, s0, s1) << "\n"; } 

输出:

 i = 3, f = 5.000000, s = hello world 

Feel free to merge this answer with the original one if desired.

One solution I've favoured is to do this with sprintf directly into the std::string buffer, after making said buffer big enough:

 #include <string> #include <iostream> using namespace std; string l_output; l_output.resize(100); for (int i = 0; i < 1000; ++i) { memset (&l_output[0], 0, 100); sprintf (&l_output[0], "\r%i\0", i); cout << l_output; cout.flush(); } 

So, create the std::string, resize it, access its buffer directly…

Poco Foundation library has a very convenient format function, which supports std::string in both the format string and the values:

You can format C++ output in cout using iomanip header file. Make sure that you include iomanip header file before you use any of the helper functions like setprecision, setfill etc.

Here is a code snippet I have used in the past to print the average waiting time in the vector, which I have "accumulated".

 #include<iomanip> #include<iostream> #include<vector> #include<numeric> ... cout<< "Average waiting times for tasks is " << setprecision(4) << accumulate(all(waitingTimes), 0)/double(waitingTimes.size()) ; cout << " and " << Q.size() << " tasks remaining" << endl; 

Here is a brief description of how we can format C++ streams. http://www.cprogramming.com/tutorial/iomanip.html

There can be problems, if the buffer is not large enough to print the string. You must determine the length of the formatted string before printing a formatted message in there. I make own helper to this (tested on Windows and Linux GCC ), and you can try use it.

String.cpp: http://pastebin.com/DnfvzyKP
String.h: http://pastebin.com/7U6iCUMa

String.cpp:

 #include <cstdio> #include <cstdarg> #include <cstring> #include <string> using ::std::string; #pragma warning(disable : 4996) #ifndef va_copy #ifdef _MSC_VER #define va_copy(dst, src) dst=src #elif !(__cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)) #define va_copy(dst, src) memcpy((void*)dst, (void*)src, sizeof(*src)) #endif #endif /// /// \breif Format message /// \param dst String to store formatted message /// \param format Format of message /// \param ap Variable argument list /// void toString(string &dst, const char *format, va_list ap) throw() { int length; va_list apStrLen; va_copy(apStrLen, ap); length = vsnprintf(NULL, 0, format, apStrLen); va_end(apStrLen); if (length > 0) { dst.resize(length); vsnprintf((char *)dst.data(), dst.size() + 1, format, ap); } else { dst = "Format error! format: "; dst.append(format); } } /// /// \breif Format message /// \param dst String to store formatted message /// \param format Format of message /// \param ... Variable argument list /// void toString(string &dst, const char *format, ...) throw() { va_list ap; va_start(ap, format); toString(dst, format, ap); va_end(ap); } /// /// \breif Format message /// \param format Format of message /// \param ... Variable argument list /// string toString(const char *format, ...) throw() { string dst; va_list ap; va_start(ap, format); toString(dst, format, ap); va_end(ap); return dst; } /// /// \breif Format message /// \param format Format of message /// \param ap Variable argument list /// string toString(const char *format, va_list ap) throw() { string dst; toString(dst, format, ap); return dst; } int main() { int a = 32; const char * str = "This works!"; string test(toString("\nSome testing: a = %d, %s\n", a, str)); printf(test.c_str()); a = 0x7fffffff; test = toString("\nMore testing: a = %d, %s\n", a, "This works too.."); printf(test.c_str()); a = 0x80000000; toString(test, "\nMore testing: a = %d, %s\n", a, "This way is cheaper"); printf(test.c_str()); return 0; } 

String.h:

 #pragma once #include <cstdarg> #include <string> using ::std::string; /// /// \breif Format message /// \param dst String to store formatted message /// \param format Format of message /// \param ap Variable argument list /// void toString(string &dst, const char *format, va_list ap) throw(); /// /// \breif Format message /// \param dst String to store formatted message /// \param format Format of message /// \param ... Variable argument list /// void toString(string &dst, const char *format, ...) throw(); /// /// \breif Format message /// \param format Format of message /// \param ... Variable argument list /// string toString(const char *format, ...) throw(); /// /// \breif Format message /// \param format Format of message /// \param ap Variable argument list /// string toString(const char *format, va_list ap) throw(); 

I gave it a try, with regular expressions . I implemented it for ints and const strings as an example, but you can add whatever other types ( POD types but with pointers you can print anything).

 #include <assert.h> #include <cstdarg> #include <string> #include <sstream> #include <regex> static std::string formatArg(std::string argDescr, va_list args) { std::stringstream ss; if (argDescr == "i") { int val = va_arg(args, int); ss << val; return ss.str(); } if (argDescr == "s") { const char *val = va_arg(args, const char*); ss << val; return ss.str(); } assert(0); //Not implemented } std::string format(std::string fmt, ...) { std::string result(fmt); va_list args; va_start(args, fmt); std::regex e("\\{([^\\{\\}]+)\\}"); std::smatch m; while (std::regex_search(fmt, m, e)) { std::string formattedArg = formatArg(m[1].str(), args); fmt.replace(m.position(), m.length(), formattedArg); } va_end(args); return fmt; } 

Here is an example of use of it:

 std::string formatted = format("I am {s} and I have {i} cats", "bob", 3); std::cout << formatted << std::endl; 

输出:

I am bob and I have 3 cats

this can be tried out. 简单。 really does not use nuances of the string class though.

 #include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <assert.h> #include <string> #include <exception> using namespace std; //--------------------------------------------------------------------- class StringFormatter { public: static string format(const char *format, ...); }; string StringFormatter::format(const char *format, ...) { va_list argptr; va_start(argptr, format); char *ptr; size_t size; FILE *fp_mem = open_memstream(&ptr, &size); assert(fp_mem); vfprintf (fp_mem, format, argptr); fclose (fp_mem); va_end(argptr); string ret = ptr; free(ptr); return ret; } //--------------------------------------------------------------------- int main(void) { string temp = StringFormatter::format("my age is %d", 100); printf("%s\n", temp.c_str()); return 0; } 
 _return.desc = (boost::format("fail to detect. cv_result = %d") % st_result).str(); 

All the answers so far here seems to have problems: (1) it may not work on VC++ (2) it requires additional dependencies like boost or fmt (3) its too complicated custom implementation and probably not tested well.

Below code addresses all above issues.

 #include <string> #include <cstdarg> #include <memory> std::string format(const char* format, ...) { va_list args; va_start(args, format); #ifndef _MSC_VER size_t size = std::snprintf( nullptr, 0, format, args) + 1; // Extra space for '\0' std::unique_ptr<char[]> buf( new char[ size ] ); std::vsnprintf( buf.get(), size, format, args); return std::string(buf.get(), buf.get() + size - 1 ); // We don't want the '\0' inside #else int size = _vscprintf(format, args); std::string result(++size, 0); vsnprintf_s((char*)result.data(), size, _TRUNCATE, format, args); return result; #endif va_end(args); } int main() { float f = 3.f; int i = 5; std::string s = "hello!"; auto rs = format("i=%d, f=%f, s=%s", i, f, s.c_str()); printf("%s", rs.c_str()); return 0; } 

笔记:

  1. Separate VC++ code branch is necessary because of non-compliance with standards.
  2. The function accepts char * instead of std::string . This because most of the time this function would be called with literal string which is indeed char * , not std::string . In case you do have std::string as format parameter, then just call .c_str() .
  3. Name of the function is format instead of things like string_format because the parameters and return type already tells you what it formats.

Tested on,

  • GCC 4.9.2/C++11/C++14
  • VC++ compiler ver 19.0
  • Clang 3.7.0