可以输出打印到FILE *的例程用于在C中构建字符串吗?

时间:2009-08-13 05:42:31

标签: c99

我有一种不好的感觉,这个问题的答案是“不”,但我想把它扔出去,万一有人有任何聪明的想法。

我有一组输出例程,它们采用复杂的数据结构并以文本格式打印。他们有原型,如:

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

我是这样编写的,这样我就可以获得有效的缓冲输出到终端,文件,网络等。

不幸的是,我不知道使用标准C99,我可以使用这些相同的例程在内存中构建一个字符串。

所以我的问题是:

  1. 有没有聪明的方法可以有效地使用fputs(),fprintf()等输出到字符串?
  2. 如果没有,是否有更好的范例可以有效地进行缓冲文件输出和字符串构建?我能想到的最好的是拥有自己的vtable结构(而不​​是FILE *)。 vtable将有一个输出函数,可以附加到字符串或调用fwrite。但是,我还必须创建printf包装器。
  3. 还有其他聪明的想法吗?

    编辑:我发现fmemopen()是POSIX.1-2008的一部分(参见:fmemopen()),但并未得到广泛支持,至少根据GNU libc手册页。

4 个答案:

答案 0 :(得分:1)

没有可行的方法来做到这一点。 glibc systems(linux)有open_memstream/fmemopen,其他系统可能没有类似的东西。

可移植的方法是写入文件并将其读回字符串。或者将问题分开。而不是实现

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

你是实施

char *mystruct_2_str(struct mystruct *s);

动态分配字符串(或传入缓冲区),将其格式化为带有标准字符串函数的字符串(snprintf等),并让调用者决定是否将其写入文件*

答案 1 :(得分:1)

如果您不关心在FILE上搜索,那么可以使用便携式(对于所有早期的POSIX目标)获得与fmemopen相同的效果,但它的成本相当高。创建一个管道和一个新的分离线程,并在调用线程中fdopen管道的写入端。然后,新线程从管道的读取端读取并将数据放入字符串缓冲区。新线程从管道中获得EOF时返回;因为它是分离的,所以没有清理工作。

答案 2 :(得分:0)

当然,FILE结构没有什么神奇之处,虽然我不知道任何内置的库函数来从字符串缓冲区创建一个。这是定义的至少一个版本。

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

您可以从字符串缓冲区自己构建其中一个结构并将其传递给您的函数,还可以创建一个可以释放内存的拆卸功能。

问题是,您可以在您的版本上使用哪些CRT库调用?显然,任何引用文件名的内容都将失败,因为没有。但你可能会使用像fwrite,fread和fseek这样的函数,它们会操纵指针并在必要时分配更多的空间。

答案 3 :(得分:0)

#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;
}