Peut-routines de sortie qui impriment un fichier * être utilisé pour construire une chaîne en C?

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

  •  13-09-2019
  •  | 
  •  

Question

J'ai un mauvais pressentiment que la réponse à cette question est « non », mais je voulais jeter ceci quelqu'un là-bas en cas a des idées intelligentes.

J'ai un ensemble de routines de sortie qui prennent une structure de données complexes et l'imprimer dans un format textuel. Ils ont des prototypes comme:

void print_mystruct(struct mystruct *s, FILE *stream)

Je l'ai écrit cette façon pour que je puisse obtenir efficace, sortie tamponnée au terminal, à un fichier, au réseau, etc.

Malheureusement, il n'y a aucun moyen que je connaisse, en utilisant C99 standard, que je peux utiliser ces mêmes routines pour construire une chaîne en mémoire.

Alors mes questions sont:

  1. est-il un moyen intelligent que je peux utiliser efficacement fputs (), fprintf (), etc. vers la sortie à une chaîne?
  2. sinon, est-il un meilleur paradigme qui peut efficacement faire à la fois la sortie de fichier en mémoire tampon et la construction chaîne? Le mieux que je peux penser est d'avoir ma propre structure avec un vtable (au lieu d'un FILE *). Le vtable aurait une fonction de sortie qui soit à ajouter une chaîne ou appelez fwrite. Mais je dois créer des emballages printf aussi.

D'autres idées intelligentes?

EDIT: J'ai découvert que fmemopen() fait partie de POSIX.1-2008 (voir: fmemopen () ) mais ne sont pas largement pris en charge, au moins en fonction de la libc GNU manpage.

Était-ce utile?

La solution

Il n'y a aucun moyen portable de le faire. systèmes glibc (linux) ont open_memstream / fmemopen , d'autres systèmes peut-être pas ont quelque chose comme ça.

La façon portable est d'écrire dans un fichier et le lire dans une chaîne. Ou de séparer les préoccupations. Au lieu de mettre en œuvre

void print_mystruct(struct mystruct *s,FILE *f);

vous deviendriez par exemple mettre en œuvre

char *mystruct_2_str(struct mystruct *s);

Ce qui alloue dynamiquement une chaîne (ou passer dans un tampon), formate à une chaîne avec des fonctions de chaîne standard (snprintf etc.) et ont l'appelant de choisir d'écrire cela à un FILE *

Autres conseils

Si vous ne se soucient pas de la recherche sur le fichier, il y a un moyen d'obtenir le même effet portable (à toutes les cibles POSIX antérieures) comme fmemopen, mais il est plutôt coûteux. Faire un tuyau et un nouveau fil détaché et fdopen l'extrémité d'écriture du tuyau dans le fil d'appel. Le nouveau thread lit ensuite à partir de la fin de la lecture de la conduite et met les données dans le tampon de chaîne. Avoir le nouveau retour de fil quand il est EOF du tuyau; car il est détaché, il n'y a pas de nettoyage à faire.

Bien sûr, il n'y a rien de magique struct FILE, bien que je ne connais pas de fonction intégrée bibliothèque pour créer un à partir d'un tampon de chaîne. Voici au moins une version de la définition.

struct _iobuf {
        char *_ptr;
        int   _cnt;
        char *_base;
        int   _flag;
        int   _file;
        int   _charbuf;
        int   _bufsiz;
        char *_tmpfname;
        };
typedef struct _iobuf FILE;

Vous pouvez fabriquer un de ces struct sur votre propre à partir d'un tampon de chaîne et le transmettre à votre fonction, et créer également une fonction de démontage qui libère la mémoire.

La question est, qui appelle la bibliothèque CRT pouvez-vous utiliser votre version? Il est évident que quelque chose se référant au nom de fichier échouera, car il n'y en a pas. Mais vous pouvez probablement utiliser des fonctions comme fwrite et fread et fseek, qui manipulera les pointeurs et en allouant plus d'espace si nécessaire.

#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;
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top