Чтение неизвестного количества строк из файла в память в C является основной необходимостью.Есть несколько способов приблизиться к этому, но стандартная практика заключается в следующем:
объявить pointer to pointer to type
(char**
для строк в файле), чтобы позволить вам собирать иссылаться на каждую строку после чтения в память;
выделять некоторое разумно ожидаемое количество указателей для начала, чтобы избежать повторных обращений к realloc
выделению указателей для каждой строки индивидуально (первоначально выделение 8, 16, 32, ..
все работает нормально);
объявляем переменную для отслеживания количества прочитанных строк и приращения для каждой строки;
читаем каждую строкуфайл в буфер (POSIX getline
работает особенно хорошо, потому что он сам будет динамически выделять достаточно памяти для обработки любой длины строки - освобождая вас от чтения с фиксированным буфером и выделяя и накапливая частичные строки до концастрока достигнута)
выделить хранилище для каждой строки, скопировать строку в новое хранилище и назначить начальные адресаs к вашему следующему указателю, strdup
делает оба для вас, но, поскольку он выделяет, убедитесь, что вы подтвердите, что он успешен;
когда ваш индекс достигнет текущего количества выделенных указателей, *На 1031 * больше указателей (как правило, удваивая число или увеличивая число на 3/2
- скорость, если увеличение не особенно важно - важно то, чтобы у вас всегда был действительный указатель для назначения нового блока памятипридерживая свою линию);и
повторять до тех пор, пока файл не будет полностью прочитан.
При перераспределении памяти необходимо учитывать некоторые тонкости.Сначала никогда не realloc
непосредственно на перераспределяемый указатель, например, не делайте:
mypointer = realloc (mypointer, current_size * 2);
, если realloc
не удастся, он возвращает NULL
, и если вы назначаете возврат к исходному указателю, вызамените адрес на ваши текущие данные с помощью NULL
, что приведет к утечке памяти.Вместо этого всегда используйте временный указатель и проверяйте realloc
успешно, прежде чем присваивать новый блок памяти исходному указателю, например,
if (filled_pointers == allocated pointers) {
void *tmp = realloc (mypointer, current_size * 2);
if (tmp == NULL) {
perror ("realloc-mypointer");
break; /* or use goto to jump out of your read loop,
* preserving access to your current data in
* the original pointer.
*/
}
mypointer = tmp;
current_size *= 2;
}
Сложив части в примере, используя getline
, вы можете сделатьчто-то вроде следующего.(примечание: код ожидает, что имя файла будет считано в качестве 1-го аргумента вашей программы, если аргумент не задан, программа будет читать из stdin
по умолчанию)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define NPTR 8 /* initial number of pointers (must be > 0) */
int main (int argc, char **argv) {
size_t ndx = 0, /* line index */
nptrs = NPTR, /* initial number of pointers */
n = 0; /* line alloc size (0, getline decides) */
ssize_t nchr = 0; /* return (no. of chars read by getline) */
char *line = NULL, /* buffer to read each line */
**lines = NULL; /* pointer to pointer to each line */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
return 1;
}
/* allocate/validate initial 'nptrs' pointers */
if (!(lines = calloc (nptrs, sizeof *lines))) {
perror ("calloc - lines");
return 1;
}
/* read each line with POSIX getline */
while ((nchr = getline (&line, &n, fp)) != -1) {
if (nchr && line[nchr - 1] == '\n') /* check trailing '\n' */
line[--nchr] = 0; /* overwrite with nul-char */
char *buf = strdup (line); /* allocate/copy line */
if (!buf) { /* strdup allocates, so validate */
perror ("strdup-line");
break;
}
lines[ndx++] = buf; /* assign start address for buf to lines */
if (ndx == nptrs) { /* if pointer limit reached, realloc */
/* always realloc to temporary pointer, to validate success */
void *tmp = realloc (lines, sizeof *lines * nptrs * 2);
if (!tmp) { /* if realloc fails, bail with lines intact */
perror ("realloc - lines");
break; /* don't exit, lines holds current lines */
}
lines = tmp; /* assign reallocted block to lines */
/* zero all new memory (optional) */
memset (lines + nptrs, 0, nptrs * sizeof *lines);
nptrs *= 2; /* increment number of allocated pointers */
}
}
free (line); /* free memory allocated by getline */
if (fp != stdin) fclose (fp); /* close file if not stdin */
for (size_t i = 0; i < ndx; i++) {
printf ("line[%3zu] : %s\n", i, lines[i]);
free (lines[i]); /* free memory for each line */
}
free (lines); /* free pointers */
return 0;
}
Просмотрите все и позвольте мнезнать, если у вас есть дополнительные вопросы.Если у вас нет getline
или strdup
, сообщите мне, и я буду рад помочь вам с реализацией, обеспечивающей их поведение.