2009-11-16 2 views
5

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

/** 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.

ответ

5

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

+0

open_memstream - именно то, что я хочу. Я не уверен, использует ли он метод связанных списков, но я не буду писать огромные суммы, поэтому это не имеет значения. – Edmund

2

Я не уверен, если это возможно, не переносимо расширить 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

+1 Хорошее объяснение о необходимость в отдельном потоке – Andomar

0

Я не уверен, что понимаю, почему вы хотите испортить 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 вопрос для предложения о том, как справиться с этим: Resuming [vf]?nprintf after reaching the limit).

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

+0

Имеются веские основания для такого «ФАЙЛА»: существует множество библиотек, которые предлагают только «ввод/вывод» на FILE *, и настаивают на чтении файла, когда данные могут быть уже доступны в памяти. Поэтому желательно иметь возможность передавать буфер памяти, а не записывать в файл только для этих библиотек. К сожалению, эти библиотеки редко, если вообще когда-либо, предлагают альтернативу их 'FILE *' -зависимому API. – greyfade

Смежные вопросы