2009-11-16 16 views
5

Sto cercando un modo per passare in un FILE * a qualche funzione in modo che la funzione possa scrivere su di esso con fprintf. Questo è facile se voglio che l'output venga visualizzato in un file reale su disco, ad esempio. Ma quello che mi piacerebbe invece è ottenere tutto l'output come una stringa (char *). Il tipo di API che vorrei è:Creazione di un flusso FILE * che risulta in una stringa

/** 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); 

Il glibc intestazioni sembrano indicare che un file può essere impostato con le funzioni di gancio per eseguire la lettura e la scrittura vera e propria. Nel mio caso, penso che voglio che il gancio di scrittura aggiunga una copia della stringa a un elenco collegato e che ci sia una funzione get_string_buffer che calcoli la lunghezza totale dell'elenco, allochi la memoria per esso e quindi copi ogni elemento in esso nel posto giusto.

Sto puntando a qualcosa che può essere passato a una funzione come do_some_stuff senza che la funzione debba sapere altro che ha un FILE * a cui è possibile scrivere.

Esiste un'implementazione esistente di qualcosa di simile? Sembra una cosa utile e C-friendly da fare, supponendo che abbia ragione sull'estensibilità FILE.

risposta

5

Se la portabilità non è importante per voi, è possibile dare uno sguardo su fmemopen e open_memstream. Sono estensioni GNU, quindi disponibili solo su sistemi glibc. Anche se sembra che facciano parte di POSIX.1-2008 (fmemopen e open_memstream).

+0

open_memstream è esattamente quello che voglio. Non sono sicuro che stia usando l'approccio della lista collegata, ma non scriverò grandi somme su di esso, quindi non importa. – Edmund

2

io non sono sicuro se è possibile non-portabile estendere FILE oggetti, ma se siete alla ricerca di qualcosa di un po 'più amichevole POSIX, è possibile utilizzare pipe e fdopen.

Non corrisponde esattamente allo FILE* che restituisce i byte da un buffer, ma è certamente un FILE* con contenuti determinati a livello di codice.

int fd[2]; 
FILE *in_pipe; 

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

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

Da lì si vuole scrivere il buffer in fd[1] utilizzando write(). Attento con questo passaggio, però, perché write() può bloccare se il buffer della pipe è pieno (cioè qualcuno deve leggere l'altra estremità) e potresti ottenere EINTR se il tuo processo riceve un segnale durante la scrittura. Fai attenzione anche a SIGPIPE, che si verifica quando l'altra estremità chiude la pipa. Forse per il tuo uso potresti voler fare il write del buffer in un thread separato per evitare il blocco e assicurarti di gestire SIGPIPE.

Naturalmente, questo non creerà un ricercabile FILE* ...

+0

+1 buona spiegazione su la necessità di una filettatura separata – Andomar

0

Non sono sicuro di aver capito perché si vuole rovinare con FILE *. Non potresti semplicemente scrivere su un file e poi caricarlo in una stringa?

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

Se si desidera solo di "scrivere" testo formattato in una stringa, un'altra opzione potrebbe essere quella di gestire un buffer estendibile utilizzando snprintf() (vedi le risposte a questa domanda SO per un suggerimento su come gestire questo: Resuming [vf]?nprintf after reaching the limit).

Se, invece, si vuole creare un tipo che può essere passato in modo trasparente a qualsiasi funzione di prendere un FILE * per farli agire sui buffer di stringa, è una questione molto più complessa ...

+0

Ci sono buone ragioni per desiderare un simile 'FILE *': Ci sono molte librerie che offrono solo I/O basato su 'FILE *' e insistono sulla lettura di un file, quando i dati potrebbero essere già disponibili in memoria. È auspicabile, quindi, essere in grado di passare il buffer di memoria anziché scrivere su un file solo per queste librerie. Sfortunatamente, queste librerie offrono raramente, se mai, un'alternativa alla loro API 'FILE *' -dipendente. – greyfade

Problemi correlati