Подтвердить что ты не робот

C Программирование: как читать содержимое всего файла в буфер

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

Какой будет самый эффективный вариант, который переносится даже на Linux.

ENV: Windows

4b9b3361

Ответ 1

Переносимость между Linux и Windows - большая головная боль, так как Linux - это POSIX-совместимая система с, как правило, надлежащим, высококачественным набором инструментов для C, тогда как Windows даже не предоставляет много функций в стандартной библиотеке C.

Однако, если вы хотите придерживаться стандарта, вы можете написать что-то вроде этого:

#include <stdio.h>
#include <stdlib.h>

FILE *f = fopen("textfile.txt", "rb");
fseek(f, 0, SEEK_END);
long fsize = ftell(f);
fseek(f, 0, SEEK_SET);  /* same as rewind(f); */

char *string = malloc(fsize + 1);
fread(string, fsize, 1, f);
fclose(f);

string[fsize] = 0;

Здесь string будет содержать содержимое текстового файла в виде правильно завершенной 0 строки C. Этот код является просто стандартным C, он не зависит от POSIX (хотя это не гарантирует, что он будет работать/компилироваться в Windows...)

Ответ 2

Вот что я бы рекомендовал.

Он должен соответствовать C89 и быть полностью переносимым. В частности, он работает также на трубах и сокетах на системах POSIXy.

Идея состоит в том, что мы читаем ввод в больших единицах (READALL_CHUNK), динамически перераспределяя буфер по мере необходимости. Мы используем только realloc(), fread(), ferror() и free():

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>

/* Size of each input chunk to be
   read and allocate for. */
#ifndef  READALL_CHUNK
#define  READALL_CHUNK  262144
#endif

#define  READALL_OK          0  /* Success */
#define  READALL_INVALID    -1  /* Invalid parameters */
#define  READALL_ERROR      -2  /* Stream error */
#define  READALL_TOOMUCH    -3  /* Too much input */
#define  READALL_NOMEM      -4  /* Out of memory */

/* This function returns one of the READALL_ constants above.
   If the return value is zero == READALL_OK, then:
     (*dataptr) points to a dynamically allocated buffer, with
     (*sizeptr) chars read from the file.
     The buffer is allocated for one extra char, which is NUL,
     and automatically appended after the data.
   Initial values of (*dataptr) and (*sizeptr) are ignored.
*/
int readall(FILE *in, char **dataptr, size_t *sizeptr)
{
    char  *data = NULL, *temp;
    size_t size = 0;
    size_t used = 0;
    size_t n;

    /* None of the parameters can be NULL. */
    if (in == NULL || dataptr == NULL || sizeptr == NULL)
        return READALL_INVALID;

    /* A read error already occurred? */
    if (ferror(in))
        return READALL_ERROR;

    while (1) {

        if (used + READALL_CHUNK + 1 > size) {
            size = used + READALL_CHUNK + 1;

            /* Overflow check. Some ANSI C compilers
               may optimize this away, though. */
            if (size <= used) {
                free(data);
                return READALL_TOOMUCH;
            }

            temp = realloc(data, size);
            if (temp == NULL) {
                free(data);
                return READALL_NOMEM;
            }
            data = temp;
        }

        n = fread(data + used, 1, READALL_CHUNK, in);
        if (n == 0)
            break;

        used += n;
    }

    if (ferror(in)) {
        free(data);
        return READALL_ERROR;
    }

    temp = realloc(data, used + 1);
    if (temp == NULL) {
        free(data);
        return READALL_NOMEM;
    }
    data = temp;
    data[used] = '\0';

    *dataptr = data;
    *sizeptr = used;

    return READALL_OK;
}

Выше, я использовал постоянный размер блока, READALL_CHUNK == 262144 (256*1024). Это означает, что в худшем случае до 262145 символов тратится впустую (выделено, но не используется), но только временно. В конце функция перераспределяет буфер до оптимального размера. Кроме того, это означает, что мы делаем четыре перераспределения на каждый мегабайт данных.

Значение по умолчанию 262144 байт в приведенном выше коде является консервативным; он хорошо работает даже для старых мини-адаптеров и малины-писа и большинства встроенных устройств с объемом памяти, доступным для этого процесса, объемом не менее нескольких мегабайт. Тем не менее, он не настолько мал, что он замедляет работу (из-за многих вызовов чтения и многих перераспределений буферов) на большинстве систем.

Для настольных компьютеров в это время (2017) я рекомендую гораздо больше READALL_CHUNK, возможно #define READALL_CHUNK 2097152 (2 MiB).

Поскольку определение READALL_CHUNK охраняется (т.е. оно определено, только если оно находится в этой точке кода еще undefined), вы можете переопределить значение по умолчанию во время компиляции, используя (в большинстве C компиляторы) -DREADALL_CHUNK=2097152 параметр командной строки - но проверьте параметры компилятора для определения макроса препроцессора с помощью параметров командной строки.

Ответ 3

Если вы заранее знаете максимальный размер буфера:

#include <stdio.h>
#define MAXBUFLEN 1000000

char source[MAXBUFLEN + 1];
FILE *fp = fopen("foo.txt", "r");
if (fp != NULL) {
    size_t newLen = fread(source, sizeof(char), MAXBUFLEN, fp);
    if (newLen == 0) {
        fputs("Error reading file", stderr);
    } else {
        source[++newLen] = '\0'; /* Just to be safe. */
    }

    fclose(fp);
}

Или, если вы этого не сделаете:

#include <stdio.h>
#include <stdlib.h>

char *source = NULL;
FILE *fp = fopen("foo.txt", "r");
if (fp != NULL) {
    /* Go to the end of the file. */
    if (fseek(fp, 0L, SEEK_END) == 0) {
        /* Get the size of the file. */
        long bufsize = ftell(fp);
        if (bufsize == -1) { /* Error */ }

        /* Allocate our buffer to that size. */
        source = malloc(sizeof(char) * (bufsize + 1));

        /* Go back to the start of the file. */
        if (fseek(fp, 0L, SEEK_SET) != 0) { /* Handle error here */ }

        /* Read the entire file into memory. */
        size_t newLen = fread(source, sizeof(char), bufsize, fp);
        if (newLen == 0) {
            fputs("Error reading file", stderr);
        } else {
            source[++newLen] = '\0'; /* Just to be safe. */
        }
    }
    fclose(fp);
}

free(source); /* Don't forget to call free() later! */

Ответ 4

Портативное решение может использовать getc.

#include <stdio.h>

char buffer[MAX_FILE_SIZE];
size_t i;

for (i = 0; i < MAX_FILE_SIZE; ++i)
{
    int c = getc(fp);

    if (c == EOF)
    {
        buffer[i] = 0x00;
        break;
    }

    buffer[i] = c;
}

Если вы не хотите иметь макрос MAX_FILE_SIZE или если это большое число (такое, что buffer было бы большим, чтобы поместиться в стек), используйте динамическое выделение.