Я хочу написать полное содержимое файла в буфер. Файл фактически содержит только строку, которую мне нужно сравнить со строкой.
Какой будет самый эффективный вариант, который переносится даже на Linux.
ENV: Windows
Я хочу написать полное содержимое файла в буфер. Файл фактически содержит только строку, которую мне нужно сравнить со строкой.
Какой будет самый эффективный вариант, который переносится даже на Linux.
ENV: Windows
Переносимость между 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...)
Вот что я бы рекомендовал.
Он должен соответствовать 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
параметр командной строки - но проверьте параметры компилятора для определения макроса препроцессора с помощью параметров командной строки.
Если вы заранее знаете максимальный размер буфера:
#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! */
Портативное решение может использовать 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
было бы большим, чтобы поместиться в стек), используйте динамическое выделение.