Pode rotinas de saída que imprimir em um arquivo * ser usado para construir uma string em C?
-
13-09-2019 - |
Pergunta
Eu tenho um mau pressentimento de que a resposta a esta pergunta é "não", mas eu queria jogar esta lá fora no caso de alguém tem quaisquer ideias inteligentes.
Eu tenho um conjunto de rotinas de saída que levam uma complexa estrutura de dados e imprimi-lo em um formato textual. Eles têm protótipos como:
void print_mystruct(struct mystruct *s, FILE *stream)
Eu escrevi-lo desta forma para que eu possa obter eficiente saída, tamponada para o terminal, para um arquivo, para a rede, etc.
Infelizmente, não há nenhuma maneira que eu conheço, utilizando C99 padrão, que eu possa usar essas mesmas rotinas para construir uma string na memória.
Então, minhas perguntas são:
- Existe alguma maneira inteligente que eu posso usar eficientemente fputs (), fprintf (), etc. a saída para uma string?
- Se não, existe um paradigma melhor que pode eficientemente fazer as duas coisas prédio saída do arquivo e corda tamponada? O melhor que eu posso pensar é ter a minha própria estrutura com uma vtable (em vez de um arquivo *). O vtable teria uma função de saída que quer anexar a uma corda ou chamada fwrite. Mas então eu teria que criar wrappers printf também.
Quaisquer outras idéias inteligentes?
EDIT: Eu descobri que fmemopen()
faz parte de POSIX.1-2008 (ver: fmemopen () ), mas não é amplamente apoiada, pelo menos de acordo com a GNU libc manpage.
Solução
Não há nenhuma maneira portátil de fazer isso. sistemas glibc (Linux) têm open_memstream / fmemopen , outros sistemas não pode ter algo parecido com isso.
A maneira portátil é escrever para um arquivo e lê-lo de volta em uma string. , Ou para separar as preocupações. Em vez de implementar
void print_mystruct(struct mystruct *s,FILE *f);
Você iria por exemplo implementar
char *mystruct_2_str(struct mystruct *s);
que aloca dinamicamente uma string (ou passar em um buffer), formatos para uma string com funções de cadeia padrão (snprintf etc.) e ter o chamador decidir se a escrever que para um arquivo *
Outras dicas
Se você não se preocupam com busca no arquivo, há um portátil (a todos os alvos anteriores POSIX) maneira de obter o mesmo efeito que fmemopen
, mas é bastante caro. Faça um cachimbo e um novo segmento individual, e fdopen
o fim da escrita do tubo no segmento chamado. O novo segmento, em seguida, lê a partir do final da leitura do tubo e coloca os dados no buffer de string. Ter o novo retorno fio quando fica EOF do tubo; uma vez que é individual, não há limpeza para fazer.
Claro, não há nada de mágico sobre a estrutura ARQUIVO, embora eu não sei de qualquer construído em função de biblioteca para criar um de um buffer de string. Aqui é pelo menos uma versão da definição.
struct _iobuf {
char *_ptr;
int _cnt;
char *_base;
int _flag;
int _file;
int _charbuf;
int _bufsiz;
char *_tmpfname;
};
typedef struct _iobuf FILE;
Você poderia fabricar uma dessas estruturas em seu próprio país de um buffer de string e passá-lo para a sua função, e também criar uma função lágrima-down que libera a memória.
A questão é, o que chamadas de biblioteca CRT você pode usar em sua versão? Obviamente, nada referindo-se ao nome do arquivo irá falhar, uma vez que não é um deles. Mas você provavelmente poderia usar funções como fwrite e fread e fseek, que será manipular os ponteiros e alocação de mais espaço, se necessário.
#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;
}