tags:

views:

972

answers:

10

Does anyone know a good safe way to redirect the output of a printf-style function to a string? The obvious ways result in buffer overflows.

Something like:

string s;
output.beginRedirect( s );  // redirect output to s

... output.print( "%s%d", foo, bar );

output.endRedirect();

I think the problem is the same as asking, "how many characters will print produce?" Ideas?

+1  A: 

Microsoft introduce the 'safe' crt functions for this.

You could use printf_s()

Stefan
Not available on any other standards conforming implementation, so yuck.
Bklyn
+11  A: 

The snprintf() function prints to a string, but only as much as the length given to it. Might be what you're looking for...

abyx
Faster than me. This is the old school way to do it.
dmckee
You can use sprintf that does not require a length. Also this will not work with strings as he asked...
Adam Peck
@abyx - spot on! It'd be good to add an example for std::string, e.g. snprintf(target, SIZE, "%s%d", foo.c_str(), bar);
orip
@Adam - sprintf is unsafe, and it's exactly what the poster asked how to avoid
orip
+15  A: 

You can use:

snprintf if you are working with a char*

stringstream if you want to use strings (not same as printf but will allow you to easily manipulate the string using the normal stream functions).

boost::format if you want a function similar to printf that will work with streams. (as per jalf in comments)

Adam Peck
and boost::format to get printf-like formatting capabilities with C++ streams
jalf
+1  A: 

With C99 you have the snprintf-function which takes the size of the buffer as a parameter. The GNU C-library has asprintf which allocates a buffer for you. For c++ though, you might be better of using iostream.

Wikipedia has more info.

Rolf Rander
+2  A: 

Old school:

snprintf()

allows you to put a limit on the number written, and return the actual written size, and

asprintf()

allocate (with malloc()) a sufficient buffer which then becomes your problem to free(). `asprintf is a GNU libc function now reimplemented in the BSD libc.

dmckee
+6  A: 

Since you've tagged this as C++ (rather than just C), I'll point out that the typical way to do this sort of thing in C++ is to use stringstream, not the printf family. No need to worry about buffer overflows with stringstreams.

The Boost Format library is also available if you like printf-style format strings but want something safer.

Kristopher Johnson
+1 for Boost Format (beat me to it :)
orip
+2  A: 

snprintf() returns the number of bytes needed to write the whole string. So, as a tiny example:

#include <strings.h>
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char** argv) 
{
 char* buf = 0;
 size_t bufsize = 0;
 size_t sz;

 const char* format="%s and %s.";
 const char* str_1 ="string 1";
 const char* str_2 ="string 2";

 sz = snprintf(buf, bufsize, format, str_1, str_2);
 printf("The buffer needs to be %d bytes.\n", sz);

 buf=malloc(sz+1);
 if(!buf) {
  printf("Can't allocate buffer!\n");
  return 1;
 }
 bufsize = sz+1;
 buf[bufsize-1] = '\0';
 sz = snprintf(buf, bufsize, format, str_1, str_2);
 printf("Filled buffer with %d bytes.\n", sz);
 printf("The buffer contains:\n'%s'\n", buf);
 return 0;
}

output:

The buffer needs to be 22 bytes.
Filled buffer with 22 bytes.
The buffer contains:
'string 1 and string 2.'
gnud
+2  A: 

This StackOverflow question has a similar discussion. Also in that question I present my favorite solution, a "format" function that takes identical arguments to printf and returns a std::string.

Larry Gritz
A: 

On Windows:

StringCchPrintf
StringCbPrintf

from strsafe.h/lib.

Johann Gerell
+1  A: 

I find the printf formatting to be very helpful and easier to use than streams. On the other hand, I do like std::string a lot too. The solution is to use sprintf, but that cannot handle arbitrary buffer size.

I've found that I need to handle common case (say, buffer limited to 256 chars) w/o overhead, and yet handle the large buffer safely. To do that, I have a buffer of 256 chars alocated in my class as a member, and I use snprinf, passing that buffer and its size. If snprintf succeeds, I can immediately retunr the formatted string. If it fails, I allocate the buffer and call snprinf again. The buffer is deallocated in the class' destructor.

Arkadiy