在 C 语言中,从文件中读取未知行数到内存中是基本必要的。有几种方法可以实现这一点,但标准做法是:
声明一个pointer to pointer to type
(char**
对于文件中的行)允许您在读入内存后收集和引用每一行;
首先分配一些合理预期数量的指针,以避免重复调用realloc
分别为每行分配指针(最初分配8, 16, 32, ..
一切正常);
声明一个变量来跟踪读取的行数,并为每行递增;
将文件的每一行读入缓冲区(POSIXgetline
工作得特别好,因为它本身会动态分配足够的存储来处理任何行长度——使您不必使用固定缓冲区进行读取,并且必须分配和累积部分行,直到到达行尾)
为每一行分配存储空间,将该行复制到新存储空间,并将起始地址分配给下一个指针,strdup
为您执行这两件事,但由于它会分配,请确保您验证它是否成功;
当你的索引达到当前分配的指针数量时,realloc
更多指针(通常通过将数字加倍,或将数字增加3/2
-- 如果增加不是特别重要,则速率 -- 重要的是确保您始终有一个有效的指针来分配保存行的新内存块);和
重复直到文件被完全读取。
重新分配内存时需要注意一些微妙之处。第一次从来没有realloc
直接指向正在重新分配的指针,例如不要做:
mypointer = realloc (mypointer, current_size * 2);
if 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
,您可以执行以下操作。 (注意:代码期望将文件名作为程序的第一个参数读取,如果没有给出参数,程序将从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
or strdup
如果可用,请告诉我,我很乐意进一步帮助实现提供其行为的实现。