Рассмотрим следующую функцию readall()
, реализованную в стандартном C:
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
char *readall(FILE *source, size_t *length)
{
char *data = NULL;
size_t size = 0;
size_t used = 0;
size_t n;
/* If we have a place to store the length,
we initialize it to zero. */
if (length)
*length = 0;
/* Do not attempt to read the source, if it is
already in end-of-file or error state. */
if (feof(source) || ferror(source))
return NULL;
while (1) {
/* Ensure there is unused chars in data. */
if (used >= size) {
const size_t new_size = (used | 65535) + 65537 - 32;
char *new_data;
new_data = realloc(data, new_size);
if (!new_data) {
/* Although reallocation failed, data is still there. */
free(data);
/* We just fail. */
return NULL;
}
data = new_data;
size = new_size;
}
/* Read more of the source. */
n = fread(data + used, 1, size - used, source);
if (!n)
break;
used += n;
}
/* Read error or other wonkiness? */
if (!feof(source) || ferror(source)) {
free(data);
return NULL;
}
/* Optimize the allocation. For ease of use, we append
at least one nul byte ('\0') at end. */
{
const size_t new_size = (used | 7) + 9;
char *new_data;
new_data = realloc(data, new_size);
if (!new_data) {
if (used >= size) {
/* There is no room for the nul. We fail. */
free(data);
return NULL;
}
/* There is enough room for at least one nul,
so no reason to fail. */
} else {
data = new_data;
size = new_size;
}
}
/* Ensure the buffer is padded with nuls. */
memset(data + used, 0, size - used);
/* Save length, if requested. */
if (length)
*length = used;
return data;
}
Она читает все из указанного дескриптора файла (который может быть стандартным потоком, таким как stdin
, или каналом, открытым через * 1006).*) в динамически размещенный буфер, добавляет нулевой байт (\0
) и возвращает указатель на буфер.Если не NULL, фактическое число прочитанных символов (т.е. не включая добавленный нуль-байт) сохраняется в size_t
, указанном вторым параметром.
Вы можете использовать его для чтения вывода двоичных данныхпо программам, скажем, dot -Tpng diagram.dot
или конвертеры изображений, или даже wget -O -
выходные данные (получение данных с определенных URL-адресов, текстовых или двоичных файлов).
Вы можете использовать это, например, таким образом:
int main(void)
{
char *src;
size_t len;
src = readall(stdin, &len);
if (!src) {
fprintf(stderr, "Error reading standard input.\n");
return EXIT_FAILURE;
}
fprintf(stderr, "Read %zu chars.\n", len);
/* As an example, print it to standard output. */
if (len > 0)
fwrite(src, len, 1, stdout);
free(src);
return EXIT_SUCCESS;
}
Функция readall()
имеет две причуды: она выделяет память примерно в 131072-байтовых блоках (но может варьироваться, если бы fread()
должна была возвращать короткие чтения), и добавляет в буфер от 7 до 15 нулевых байтов.(Есть причины, по которым мне нравится делать это таким образом, но все они носят умозрительный характер и специфичны для библиотек C, которые я склонен использовать, поэтому это не важно.)
Хотя те, что использовались выше, работают нормально, выможете изменить size_new
расчеты, если вы предпочитаете иначе.Просто убедитесь, что они оба по крайней мере used + 1
.