Possono routine di output che stampare su un file * essere utilizzati per costruire una stringa in C?
-
13-09-2019 - |
Domanda
Ho una brutta sensazione che la risposta a questa domanda è "no", ma volevo buttare questo là fuori nel caso in cui qualcuno ha qualche idea intelligente.
Ho una serie di routine di output che prendono una struttura dati complessa e stamparlo in formato testuale. Hanno prototipi come:
void print_mystruct(struct mystruct *s, FILE *stream)
ho scritto in questo modo in modo che posso ottenere efficiente, uscita tamponata al terminale, in un file, alla rete, ecc.
Purtroppo, non c'è modo che io sappia, utilizzando standard di C99, che posso usare queste stesse routine per costruire una stringa nella memoria.
Quindi le mie domande sono le seguenti:
- esiste un modo intelligente che posso utilizzare in modo efficiente fputs (), fprintf (), ecc per l'uscita in una stringa?
- se non, c'è un paradigma meglio che può fare entrambe le cose in modo efficiente costruzione file di output e la stringa tamponata? Il meglio che posso pensare è quello di avere la mia propria struttura con una vtable (invece di un file *). Vtable avrebbe una funzione di uscita che sia aggiungere a una stringa o chiamare fwrite. Ma poi avrei dovuto creare involucri printf anche.
Tutte le altre idee intelligenti?
EDIT: ho scoperto che fmemopen()
fa parte di POSIX.1-2008 (vedi: fmemopen () ), ma non è ampiamente supportato, almeno secondo libc manpage GNU.
Soluzione
Non c'è modo di fare questo portatile. sistemi di glibc (Linux) hanno open_memstream / fmemopen , gli altri sistemi non potrebbero avere qualcosa di simile.
Il modo portatile è di scrivere su un file e leggere di nuovo in una stringa. , O per separare le preoccupazioni. Invece di attuazione
void print_mystruct(struct mystruct *s,FILE *f);
Faresti esempio implementare
char *mystruct_2_str(struct mystruct *s);
Il che alloca dinamicamente una stringa (o si passa in un buffer), formati in una stringa con le funzioni di stringa standard (snprintf ecc) e hanno il chiamante decidere di scrivere che in un file *
Altri suggerimenti
Se non si preoccupano che cercano sul file, c'è un portatile (a tutti i target POSIX precedenti) modo per ottenere lo stesso effetto di fmemopen
, ma è piuttosto costoso. Fare un tubo e un nuovo thread distaccato, e fdopen
la fine scrittura del tubo nel thread chiamante. Il nuovo thread legge poi dalla fine lettura del tubo e inserisce i dati nel buffer di stringa. Avere il nuovo ritorno del filo quando si arriva EOF dal tubo; dal momento che è indipendente, non c'è pulizia da fare.
Certo, non c'è nulla di magico struct FILE, anche se non conosco nessun costruito in funzione di libreria per creare uno da un buffer di stringa. Ecco almeno una versione della definizione.
struct _iobuf {
char *_ptr;
int _cnt;
char *_base;
int _flag;
int _file;
int _charbuf;
int _bufsiz;
char *_tmpfname;
};
typedef struct _iobuf FILE;
Si potrebbe realizzare uno di questi le strutture da soli da un buffer di stringa e passarlo alla funzione, e creare anche una funzione di strappo verso il basso che libera la memoria.
La domanda è, che le chiamate libreria CRT è possibile utilizzare sul vostra versione? Ovviamente nulla riferimento al nome del file avrà esito negativo, dal momento che non ce n'è uno. Ma probabilmente si potrebbe utilizzare funzioni come fwrite e fread e fseek, che saranno manipolando i puntatori e assegnazione di più spazio, se necessario.
#include <stdio.h>
#include <stdlib.h>
char* file_to_string(FILE *f, int *len) {
if (fseek(f, 0, SEEK_END)) handle_error();
int buflen = ftell(f);
if (len) *len = buflen;
char *buf = malloc(buflen + 1);
buf[buflen] = '\0';
rewind(f);
size_t readlen = fread(buf, 1, buflen, f);
if (readlen != buflen) handle_error();
// in particular, note readlen might not equal buflen in the face of text-mode
// conversions, but tmpfile() indicates binary-mode, so doesn't affect this
// case
return buf;
}
int main() {
FILE *temp = tmpfile();
// use FILE interface
fputs("written to temporary file\n", temp);
char *s = file_to_string(temp, NULL);
// now you have contents as a string
free(s);
// can also rewind the FILE and process it incrementally, etc.
return 0;
}