I have to format std::string with sprintf and send it into file stream. How can I do this?
C++20 will include std::format which resembles sprintf
in terms of API but is fully type-safe, works with user-defined types, and uses Python-like format string syntax. Here's how you will be able to format std::string
and write it to a stream:
std::string s = "foo";
std::cout << std::format("Look, a string: {}", s);
or
std::string s = "foo";
puts(std::format("Look, a string: {}", s).c_str());
Alternatively, you could use the {fmt} library to format a string and write it to stdout
or a file stream in one go:
fmt::print(f, "Look, a string: {}", s); // where f is a file stream
As for sprintf
or most of the other answers here, unfortunately they use varargs and are inherently unsafe unless you use something like GCC's format
attribute which only works with literal format strings. You can see why these functions are unsafe on the following example:
std::string format_str = "%s";
string_format(format_str, format_str[0]);
where string_format
is an implementation from the Erik Aronesty's answer. This code compiles, but it will most likely crash when you try to run it:
$ g++ -Wall -Wextra -pedantic test.cc
$ ./a.out
Segmentation fault: 11
Disclaimer: I'm the author of {fmt} and C++20 std::format
.
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);
}
You can't do it directly, because you don't have write access to the underlying buffer (until C++11; see Dietrich Epp's comment). You'll have to do it first in a c-string, then copy it into a std::string:
char buff[100];
snprintf(buff, sizeof(buff), "%s", "Hello");
std::string buffAsStdStr = buff;
But I'm not sure why you wouldn't just use a string stream? I'm assuming you have specific reasons to not just do this:
std::ostringstream stringStream;
stringStream << "Hello";
std::string copyOfStr = stringStream.str();
C++11 solution that uses vsnprintf()
internally:
#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;
}
A safer and more efficient (I tested it, and it is faster) approach:
#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());
}
The fmt_str
is passed by value to conform with the requirements of va_start
.
NOTE: The "safer" and "faster" version doesn't work on some systems. Hence both are still listed. Also, "faster" depends entirely on the preallocation step being correct, otherwise the strcpy
renders it slower.
boost::format() provides the functionality you want:
As from the Boost format libraries synopsis:
A format object is constructed from a format-string, and is then given arguments through repeated calls to operator%. Each of those arguments are then converted to strings, who are in turn combined into one string, according to the format-string.
#include <boost/format.hpp>
cout << boost::format("writing %1%, x=%2% : %3%-th try") % "toto" % 40.23 % 50;
// prints "writing toto, x=40.230 : 50-th try"
This is how google does it: StringPrintf (BSD License)
and facebook does it in a quite similar fashion: StringPrintf (Apache License)
Both provide with a convenient StringAppendF
too.