Можно ли использовать процедуры вывода, выводящие в ФАЙЛ*, для построения строки в C?

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

  •  13-09-2019
  •  | 
  •  

Вопрос

У меня плохое предчувствие, что ответ на этот вопрос — «нет», но я хотел высказать это на случай, если у кого-нибудь возникнут умные идеи.

У меня есть набор процедур вывода, которые принимают сложную структуру данных и печатают ее в текстовом формате.У них есть прототипы, такие как:

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

Я написал это таким образом, чтобы иметь возможность получать эффективный буферизованный вывод на терминал, в файл, в сеть и т. д.

К сожалению, я не знаю способа, используя стандарт C99, чтобы я мог использовать те же самые процедуры для создания строки в памяти.

Итак, мои вопросы:

  1. есть ли какой-нибудь умный способ эффективно использовать fputs(), fprintf() и т. д.вывести в строку?
  2. если нет, существует ли лучшая парадигма, которая может эффективно выполнять как вывод буферизованных файлов, так и построение строк?Лучшее, о чем я могу думать, — это иметь собственную структуру с виртуальной таблицей (вместо ФАЙЛА*).У vtable будет функция вывода, которая либо добавит строку, либо вызовет fwrite.Но тогда мне также придется создать обертки printf.

Есть еще умные идеи?

РЕДАКТИРОВАТЬ:Я обнаружил, что fmemopen() является частью POSIX.1-2008 (см.: fmemopen()), но не поддерживается широко, по крайней мере, согласно справочной странице GNU libc.

Это было полезно?

Решение

Портативного способа сделать это не существует.системы glibc (linux) имеют open_memstream/fmemopen , в других системах может не быть чего-то подобного.

Портативный способ — записать в файл и прочитать его обратно в строку., или разделить проблемы.Вместо реализации

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

Вы бы, например.осуществлять

char *mystruct_2_str(struct mystruct *s);

Который динамически выделяет строку (или передает ее в буфер), форматирует ее в строку с помощью стандартных строковых функций (snprintf и т. д.) и позволяет вызывающей стороне решить, записывать ли ее в ФАЙЛ*

Другие советы

Если вас не интересует поиск в ФАЙЛЕ, есть переносимый (для всех более ранних целей POSIX) способ получить тот же эффект, что и fmemopen, но это довольно дорого.Сделайте трубу и новую отсоединенную резьбу, и fdopen записывающий конец канала в вызывающем потоке.Затем новый поток считывает данные из конца канала чтения и помещает данные в строковый буфер.Пусть новый поток вернется, когда он получит EOF из канала;поскольку он отсоединен, очистка не требуется.

Конечно, в структуре 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, которые будут манипулировать указателями и при необходимости выделять больше места.

#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;
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top