19

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?

Adam Peck
  • 6,930
  • 3
  • 23
  • 27
user48956
  • 14,850
  • 19
  • 93
  • 154
  • 2
    Marking the question that has been asked earlier as duplicate makes not sense. Voting to reopen (and we should close the other as duplicate). – vitaut May 23 '19 at 01:37
  • Just `size_t nbytes = snprintf(NULL, 0, "%s", name) + 1; /* +1 for the '\0' */ char *str = malloc(nbytes); snprintf(str, nbytes, "%s", name);` – user26742873 Aug 08 '20 at 15:08

11 Answers11

31

You can use:

std::snprintf if you are working with a char*

std::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)

fmt::format which is has been standardized since c++20 std::format

Mark Loyman
  • 1,983
  • 1
  • 14
  • 23
Adam Peck
  • 6,930
  • 3
  • 23
  • 27
11

The snprintf() function prints to a string, but only as much as the length given to it.

Might be what you're looking for...

Paul Rooney
  • 20,879
  • 9
  • 40
  • 61
abyx
  • 69,862
  • 18
  • 95
  • 117
8

The fmt library provides fmt::sprintf function that performs printf-compatible formatting (including positional arguments according to POSIX specification) and returns the result as an std::string:

std::string s = fmt::sprintf( "%s%d", foo, bar );

Disclaimer: I'm the author of this library.

vitaut
  • 49,672
  • 25
  • 199
  • 336
5

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
  • 81,409
  • 55
  • 245
  • 302
5

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
  • 77,584
  • 5
  • 64
  • 78
4

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 --- ex-moderator kitten
  • 98,632
  • 24
  • 142
  • 234
4

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.

Community
  • 1
  • 1
Larry Gritz
  • 13,331
  • 5
  • 42
  • 42
1

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
  • 3,221
  • 20
  • 21
1

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.

0

On Windows:

StringCchPrintf
StringCbPrintf

from strsafe.h/lib.

Johann Gerell
  • 24,991
  • 10
  • 72
  • 122
-1

Microsoft introduce the 'safe' crt functions for this.

You could use printf_s()

Stefan
  • 43,293
  • 10
  • 75
  • 117
  • 4
    Not available on any other standards conforming implementation, so yuck. – Bklyn Jan 12 '09 at 22:18
  • Also, the only difference is that the "safe" crt will crash themselves if they detect certain undefined behaviors. That doesn't bypass the issue he's encountering. – Mooing Duck Aug 19 '14 at 19:47