2009-11-16 21 views
5

Estoy buscando una manera de pasar en un FILE * a alguna función para que la función pueda escribir con fprintf. Esto es fácil si quiero que la salida aparezca en un archivo real en el disco, por ejemplo. Pero lo que me gustaría en cambio es obtener toda la salida como una cadena (char *). El tipo de API que me gustaría es:Creando una secuencia FILE * que da como resultado una cadena

/** Create a FILE object that will direct writes into an in-memory buffer. */ 
FILE *open_string_buffer(void); 

/** Get the combined string contents of a FILE created with open_string_buffer 
    (result will be allocated using malloc). */ 
char *get_string_buffer(FILE *buf); 

/* Sample usage. */ 
FILE *buf; 
buf = open_string_buffer(); 
do_some_stuff(buf); /* do_some_stuff will use fprintf to write to buf */ 
char *str = get_string_buffer(buf); 
fclose(buf); 
free(str); 

la glibc cabeceras parecen indicar que un archivo se puede configurar con funciones de enlace para realizar la lectura y la escritura real. En mi caso, creo que quiero que el gancho de escritura anexe una copia de la cadena a una lista vinculada, y que haya una función get_string_buffer que resuelva la longitud total de la lista, le asigne memoria y luego copie cada elemento en él en el lugar correcto.

Me refiero a algo que se puede pasar a una función como do_some_stuff sin que esa función necesite saber nada más que tiene un FILE * al que puede escribir.

¿Existe una implementación existente de algo como esto? Parece una cosa útil y amigable para C, suponiendo que tenga razón sobre la extensibilidad FILE.

Respuesta

5

Si la portabilidad no es importante para usted, se puede echar un vistazo en fmemopen y open_memstream. Son extensiones de GNU, por lo tanto, solo están disponibles en sistemas glibc. Aunque parece que son parte de POSIX.1-2008 (fmemopen y open_memstream).

+0

open_memstream es exactamente lo que quiero. No estoy seguro de si está utilizando el enfoque de lista enlazada, pero no voy a escribir grandes cantidades para que no importe. – Edmund

2

no estoy seguro si es posible no portable extender FILE objetos, pero si usted está buscando algo un poco más amigable POSIX, puede utilizar pipe y fdopen.

No es exactamente lo mismo que tener un FILE* que devuelve bytes de un búfer, pero sin duda es un FILE* con contenido determinado por programación.

int fd[2]; 
FILE *in_pipe; 

if (pipe(fd)) 
{ 
    /* TODO: handle error */ 
} 

in_pipe = fdopen(fd[0], "r"); 
if (!in_pipe) 
{ 
    /* TODO: handle error */ 
} 

A partir de ahí tendrá que escribir su memoria intermedia en fd[1] usando write(). Sin embargo, tenga cuidado con este paso, ya que write() puede bloquear si el buffer de la tubería está lleno (es decir, alguien necesita leer el otro extremo), y puede obtener EINTR si su proceso recibe una señal mientras escribe. También tenga cuidado con SIGPIPE, que ocurre cuando el otro extremo cierra la tubería. Tal vez para su uso, es posible que desee hacer el write del búfer en un hilo separado para evitar el bloqueo y asegúrese de manejar SIGPIPE.

Por supuesto, esto no va a crear un reubicable FILE* ...

+0

1 Buena explicación acerca la necesidad de un hilo separado – Andomar

0

No estoy seguro de entender por qué quiere meterse con FILE *. ¿No podría simplemente escribir en un archivo y luego cargarlo en una cadena?

char *get_file_in_buf(char *filename) { 
    char *buffer; 
    ... get file size with fseek or fstat ... 
    ... allocate buffer ... 
    ... read buffer from file ... 
    return buffer; 
} 

Si sólo quiere "escribir" texto con formato en una cadena, otra opción podría ser para manejar una memoria intermedia extensible usando snprintf() (ver las respuestas a esta cuestión de forma para una sugerencia sobre cómo manejar esto: Resuming [vf]?nprintf after reaching the limit)

En cambio, si desea crear un tipo que se puede pasar de forma transparente a cualquier función que toma un FILE * hacerlos actuar en memorias intermedias de cadena, que es una cuestión mucho más compleja ...

+0

Existen buenas razones para querer un 'ARCHIVO *': hay muchas bibliotecas que ofrecen solo E/S basadas en 'ARCHIVO * 'e insisten en leer un archivo cuando es posible que los datos ya estén disponibles en la memoria. Por lo tanto, es deseable poder pasar el búfer de memoria en lugar de escribir en un archivo solo para estas bibliotecas. Desafortunadamente, estas bibliotecas rara vez, si acaso, ofrecen una alternativa a su API 'FILE *' -pendent. – greyfade

Cuestiones relacionadas