Pode rotinas de saída que imprimir em um arquivo * ser usado para construir uma string em C?

StackOverflow https://stackoverflow.com/questions/1270212

  •  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:

  1. Existe alguma maneira inteligente que eu posso usar eficientemente fputs (), fprintf (), etc. a saída para uma string?
  2. 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.

Foi útil?

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;
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top