2009-01-12 10 views
12

¿Alguien sabe una buena manera segura de redirigir la salida de una función de estilo printf a una cadena? Las formas obvias resultan en desbordamientos de búfer.¿La mejor manera de imprimir de forma segura en una cadena?

Algo así como:

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

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

output.endRedirect(); 

Creo que el problema es el mismo que preguntar, "¿cuántos caracteres se imprimirá producir?" Ideas?

Respuesta

4

This StackOverflow question tiene una discusión similar. También en esa pregunta presento mi solución favorita, una función de "formato" que toma argumentos idénticos para printf y devuelve una cadena std ::.

0

Microsoft presenta las funciones de crt 'safe' para esto.

Usted podría utilizar printf_s()

+2

No disponible en ninguna otra norma que cumpla con la implementación, así que asqueroso. – Bklyn

+0

Además, la única diferencia es que el crt "seguro" se bloqueará si detectan ciertos comportamientos indefinidos. Eso no evita el problema que está enfrentando. –

11

Los snprintf() función imprime en una cadena, pero sólo tanto como la longitud dada a la misma.

podría ser lo que está buscando ...

+0

Más rápido que yo. Esta es la forma de la vieja escuela de hacerlo. – dmckee

+0

Puede usar sprintf que no requiere una longitud. Además, esto no funcionará con las cadenas, ya que le preguntó ... –

+1

@abyx - ¡apunte! Sería bueno agregar un ejemplo para std :: string, p. Ej. snprintf (destino, TAMAÑO, "% s% d", foo.c_str(), barra); – orip

24

Se puede utilizar:

std::snprintf si se está trabajando con un char *

std::stringstream si desea utilizar cadenas (no mismos como printf, pero le permitirá manipular fácilmente la cadena utilizando las funciones de transmisión normales).

boost::format si desea una función similar a printf que funcione con transmisiones. (según jalf en los comentarios)

+2

y boost :: format para obtener capacidades de formateo tipo printf con las transmisiones de C++ – jalf

1

Con C99 usted tiene la función snprintf que toma el tamaño del buffer como parámetro. La biblioteca GNU C-tiene asprintf que asigna un buffer para usted. Sin embargo, para C++, es mejor que uses iostream.

Wikipedia tiene más información.

4

vieja escuela:

snprintf() 

le permite poner un límite en el número escrito, y volver al tamaño real por escrito, y

asprintf() 

asignar (con malloc()) un tampón suficiente que luego se convierte en tu problema al free(). `asprintf es una función libc de GNU ahora reimplementada en BSD libc.

6

Dado que ha etiquetado esto como C++ (en lugar de solo C), señalaré que la forma típica de hacer este tipo de cosas en C++ es usar stringstream, no la familia printf. No hay necesidad de preocuparse por desbordamientos de búfer con stringstreams.

La biblioteca Boost Format también está disponible si le gustan las cadenas de formato de estilo printf, pero quiere algo más seguro.

+0

+1 para el formato Boost (le doy la vuelta) – orip

3

snprintf() devuelve el número de bytes necesarios para escribir la cadena completa. Por lo tanto, como un pequeño ejemplo:

#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; 
} 

de salida:

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

En Windows:

StringCchPrintf 
StringCbPrintf 

de strsafe.h/lib.

1

Encuentro que el formato printf es muy útil y más fácil de usar que las transmisiones. Por otro lado, me gusta std :: string también. La solución es usar sprintf, pero no puede manejar el tamaño de búfer arbitrario.

He encontrado que necesito manejar casos comunes (por ej., Memoria intermedia limitada a 256 caracteres) sin la sobrecarga de , y aún así manejar la memoria intermedia de gran tamaño de forma segura. Para hacer eso, tengo un buffer de 256 caracteres alocated en mi clase como miembro, y uso snprinf, pasando ese buffer y su tamaño. Si snprintf tiene éxito, puedo resintonizar inmediatamente la cadena formateada. Si falla, asigno el búfer y vuelvo a llamar a snprinf. El buffer está desasignado en el destructor de la clase.

3

The fmt library proporciona fmt::sprintf función que realiza el formateo printf-compatible (incluyendo argumentos posicionales de acuerdo con POSIX specification) y devuelve el resultado como un std::string:

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

de exención de responsabilidad: Soy el autor de esta biblioteca.

Cuestiones relacionadas