FMEMOPEN(3)

ИМЯ
fmemopen, open_memstream, open_wmemstream - открывает память как поток

ОБЗОР
#include <stdio.h>
FILE *fmemopen(void *buf, size_t size, const char *mode);
FILE *open_memstream(char **ptr, size_t *sizeloc);
#include <wchar.h>
FILE *open_wmemstream(wchar_t **ptr, size_t *sizeloc);

Требования макроса тестирования свойств для glibc (см. feature_test_macros(7)):

fmemopen(), open_memstream(), open_wmemstream():
    Начиная с glibc 2.10:
        _XOPEN_SOURCE >= 700 || _POSIX_C_SOURCE >= 200809L
    До glibc 2.10:
        _GNU_SOURCE

ОПИСАНИЕ
Функция fmemopen() открывает поток, тип доступа указывается в mode. Поток позволяет выполнять операции ввода-вывода со строкой или буфером памяти, указанным в buf. Этот буфер должен быть не менее size байт.

Значения аргумента mode такие же как у fopen(3). Если в mode указывается режим добавления, то начальной файловой позиции присваивается расположение в буфере первого байта null («\0»); в противном случае начальная файловая позиция равна началу буфера. Начиная с glibc 2.9, символ «b» можно указать вторым символом в mode. Это включает «двоичный» режим: запись неявно не добавляет завершающий байт null, и fseek(3) SEEK_END считается относительно конца буфера (т. е., значение, указанное в аргументе size), а не длины текущей строки.

Когда поток, открытый на запись, сбрасывается (flushed) (fflush(3)) или закрывается (fclose(3)), то в конец буфера записывается байт null, если есть место. Вызывающий должен быть уверен, что в буфере есть место для дополнительного байта (и в size учитывается этот байт), чтобы это произошло. Попытка записать более size байт в буфер приводит к ошибке (по умолчанию, такие ошибки будут видимы только в момент сброса буфера stdio. Следующий вызов отключает буферизацию, что может быть полезно для обнаружения ошибок в момент операции вывода:
    setbuf(stdream, NULL); Или же вызывающий может явно задать buf в качестве потокового буфера stdio, и проинформировать stdio о размере буфера с помощью:
    setbuffer(stream, buf, size);

В потоке, открытом на чтение, при обнаружении байтов null («\0») в буфере операции чтения не возвращают конец файла. Чтение из буфера будет возвращать конец файла, только когда значение файлового указателя достигнет size байт от начала буфера.

Если значение buf равно NULL, то fmemopen() динамически выделяет буфер длиной size байт. Это полезно для приложений, которым нужно записать данные во временный буфер и причитать из него. Буфер автоматически освобождается при закрытии потока. Заметим, что вызывающий никогда не получит указатель на временный буфер, выделенный этим вызовом (но смотрите open_memstream() далее). Функция open_memstream() открывает поток для записи в буфер. Буфер динамически выделяется (с помощью malloc(3)) и автоматически расширяется при необходимости. После закрытия потока вызывающий должен освободить этот буфер с помощью free(3). При закрытии потока (fclose(3)) или сброса (fflush(3)) значение расположений ptr и sizeloc обновляются и содержат, соответственно, указатель на буфер и текущий размер буфера. Эти значения остаются корректными, пока вызывающий не произведёт вывод в поток. После выполнения вывода, перед тем как обращаться к этим переменным, поток снова должен быть сброшен. Байт null поддерживается в конце буфера. Этот байт не учитывается в значении размера, хранящемся в sizeloc. Файловая позиция в потоке может быть изменена с помощью fseek(3) или fseeko(3). Перемещение файловой позиции за конец данных сразу приводит к заполнению промежутка нулями. Функция open_wmemstream() похожа на open_memstream(), но работает с широкими символами, а не с байтами.

ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ
При успешном выполнении fmemopen(), open_memstream() и open_wmemstream() возвращается указатель FILE. В противном случае возвращается NULL и errno присваивается код ошибки.

ВЕРСИИ
Функции fmemopen() и open_memstream() были доступны уже в glibc 1.0.x. Функция open_wmemstream() доступна в glibc начиная с версии 2.4.

СООТВЕТСТВИЕ СТАНДАРТАМ
POSIX.1-2008. Эти функции не определены в POSIX.1-2001 и не широко распространены в других системах. В POSIX.1-2008 указано, что символ «b» в mode должен игнорироваться. Однако в Technical Corrigendum 1 изменили стандарт, позволив реализации решать что делать; это позволяет glibc учитывать «b».

ЗАМЕЧАНИЯ
У файлового потока, возвращаемого этими функциями, отсутствует файловый дескриптор (т. е., если файловый поток передать в fileno(3), то произойдёт ошибка).

ДЕФЕКТЫ
В glibc до версии 2.7 перемещение за конец потока, созданного open_memstream(), не приводило к увеличению буфера; вместо этого вызов fseek(3) завершался с ошибкой, возвращая -1. Если указано значение size равное нулю, то fmemopen() завершится с ошибкой EINVAL. В этом случае было бы целесообразней создавать поток, который затем возвращал бы конец файла при первой попытке чтения. Кроме того, в POSIX.1-2008 ничего не сказано про ошибку в этом случае. В режиме добавления («a» или «a+») fmemopen() устанавливает начальную файловую позицию на первый байт null, но (если файловое смещение сбрасывается в расположение, отличное от конца потока) не заставляет последующие операции записи выполнять добавление в конец потока. Если в аргументе mode в fmemopen() включено добавление («a» или «a+») и аргумент size не учитывает байт null в buf, то согласно POSIX.1-2008 начальная файловая позиция должна указывать на следующий байт за концом буфера. Однако, в этом случае glibc fmemopen() присваивает файловой позиции значение -1. Для задания двоичного режима в fmemopen() вторым символом в mode должен быть «b». То есть, например, «wb+» сработает, а «w+b» — нет. Это не совпадает с трактовкой mode в fopen(3). При дополнении fmemopen() в glibc 2.9 «двоичным» режимом было потихоньку изменено ABI: раньше fmemopen() игнорировала «b» в mode.

ПРИМЕР
Программа, показанная ниже, использует fmemopen() для открытия входного буфера и open_memstream() для открытия выходного буфера с динамически изменяющимся размером. Программа сканирует входную строку (первый аргумент командной строки программы), читая целые числа, и записывает квадраты этих чисел в выходной буфер. Пример результата работы программы:
$ ./a.out '1 23 43'
size=11; ptr=1 529 1849

  Исходный код программы
#define _GNU_SOURCE
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#define handle_error(msg) \
    do { perror(msg); exit(EXIT_FAILURE); } while (0)
int
main(int argc, char *argv[])
{
    FILE *out, *in;
    int v, s;
    size_t size;
    char *ptr;
    if (argc != 2) {
        fprintf(stderr, "Использование: %s <файл>\n", argv[0]);
        exit(EXIT_FAILURE);
    }
    in = fmemopen(argv[1], strlen(argv[1]), "r");
    if (in == NULL)
        handle_error("fmemopen");
    out = open_memstream(&ptr, &size);
    if (out == NULL)
        handle_error("open_memstream");
    for (;;) {
        s = fscanf(in, "%d", &v);
        if (s <= 0)
            break;
        s = fprintf(out, "%d ", v * v);
        if (s == -1)
            handle_error("fprintf");
    }
    fclose(in);
    fclose(out);
    printf("size=%zu; ptr=%s\n", size, ptr);
    free(ptr);
    exit(EXIT_SUCCESS);
}

СМОТРИТЕ ТАКЖЕ
fopen(3), fopencookie(3)