Создание потока FILE *, который приводит к строке - PullRequest
5 голосов
/ 16 ноября 2009

Я ищу способ передать FILE * какой-либо функции, чтобы функция могла записать в нее значение fprintf. Это легко, если я хочу, чтобы вывод отображался в реальном файле на диске, скажем. Но вместо этого я хотел бы получить весь вывод в виде строки (char *). Тип API, который мне нужен:

/** Create a FILE object that will direct writes into an in-memory buffer. */
FILE *open_string_buffer(void);

/** Get the combined string contents of a FILE created with open_string_buffer
    (result will be allocated using malloc). */
char *get_string_buffer(FILE *buf);

/* Sample usage. */
FILE *buf;
buf = open_string_buffer();
do_some_stuff(buf);   /* do_some_stuff will use fprintf to write to buf */
char *str = get_string_buffer(buf);
fclose(buf);
free(str);

Заголовки glibc , кажется, указывают, что ФАЙЛ может быть установлен с функциями ловушки для выполнения фактического чтения и записи. В моем случае я думаю, что я хочу, чтобы ловушка записи добавляла копию строки к связанному списку, и чтобы существовала функция get_string_buffer, которая вычисляет общую длину списка, выделяет для него память и затем копирует каждый предмет в него в правильном месте.

Я стремлюсь к чему-то, что может быть передано в функцию, такую ​​как do_some_stuff, и этой функции не нужно знать ничего, кроме того, что она имеет FILE *, в которую она может писать.

Существует ли существующая реализация чего-то подобного? Это кажется полезным и дружественным к C вещам - при условии, что я прав насчет расширяемости FILE.

Ответы [ 3 ]

5 голосов
/ 16 ноября 2009

Если переносимость не важна для вас, вы можете посмотреть fmemopen и open_memstream . Они являются расширениями GNU, поэтому доступны только в системах glibc. Хотя, похоже, они являются частью POSIX.1-2008 ( fmemopen и open_memstream ).

2 голосов
/ 16 ноября 2009

Я не уверен, что можно без возможности расширения FILE объектов, но если вы ищете что-то более дружественное к POSIX, вы можете использовать pipe и fdopen.

Это не совсем то же самое, что FILE*, который возвращает байты из буфера, но это, безусловно, FILE* с программно определенным содержимым.

int fd[2];
FILE *in_pipe;

if (pipe(fd))
{
   /* TODO: handle error */
}

in_pipe = fdopen(fd[0], "r");
if (!in_pipe)
{
   /* TODO: handle error */
}

Оттуда вы захотите записать свой буфер в fd[1], используя write(). Осторожнее с этим шагом, потому что write() может блокироваться, если буфер канала заполнен (т.е. кто-то должен прочитать другой конец), и вы можете получить EINTR, если ваш процесс получает сигнал во время записи. Также следите за SIGPIPE, что происходит, когда другой конец закрывает трубу. Возможно, для вашего использования вы можете захотеть сделать write буфера в отдельном потоке, чтобы избежать блокировки и убедиться, что вы обрабатываете SIGPIPE.

Конечно, это не создаст поиск FILE* ...

0 голосов
/ 16 ноября 2009

Я не уверен, что понимаю, почему вы хотите испортить FILE *. Не могли бы вы просто записать в файл и затем загрузить его в строку?

 char *get_file_in_buf(char *filename) {
   char *buffer;
   ... get file size with fseek or fstat ...
   ... allocate buffer ...
   ... read buffer from file ...
   return buffer;
 }

Если вы хотите «записать» только форматированный текст в строку, другой вариант может заключаться в обработке расширяемого буфера с использованием snprintf() (см. Ответы на этот вопрос SO, чтобы узнать, как это сделать: Возобновление [vf]? Nprintf после достижения лимита ).

Если вместо этого вы хотите создать тип, который можно прозрачно передать любой функции, взяв FILE *, чтобы заставить их воздействовать на строковые буферы, это гораздо более сложный вопрос ...

...