该脚本成功打印了文本文件,但是我想将文本文件中的内容存储到一个数组中,我已经查看了很多地方,但我并不完全理解我遇到的信息,无论如何我可以获得一些指导?
#include <stdlib.h>
int main()
{
// OPENS THE FILE
FILE *fp = fopen("/classes/cs3304/cs330432/Programs/StringerTest/people.txt", "r");
size_t len = 1000;
char *word = malloc(sizeof(char) * len);
// CHECKS IF THE FILE EXISTS, IF IT DOESN'T IT WILL PRINT OUT A STATEMENT SAYING SO
if (fp == NULL)
{
printf("file not found");
return 0;
}
while(fgets(word, len, fp) != NULL)
{
printf("%s", word);
}
free(word);
}
Run Code Online (Sandbox Code Playgroud)
文本文件包含以下内容(只是单词列表):
endorse
vertical
glove
legend
scenario
kinship
volunteer
scrap
range
elect
release
sweet
company
solve
elapse
arrest
witch
invasion
disclose
professor
plaintiff
definition
bow
chauvinist
Run Code Online (Sandbox Code Playgroud)
让我们看看能否让您理清思路。首先,您的思考方向是正确的,并且应该赞扬您使用fgets()将每一行读入固定缓冲区(字符数组),然后您需要收集并存储所有行,以便它们可供使用你的程序——这似乎是轮子掉下来的地方。
方法的基本概要
总而言之,当您想要处理无限数量的行时,您需要分配和管理两种不同类型的内存块。第一个是您分配的内存块,它将保存一定数量的指针(您将存储的每一行一个指针)。您最初分配的数量并不重要,因为您将跟踪分配的数量(可用数量)和使用的数量。当(used == available)你需要realloc()更大的内存块来容纳更多的指针并继续下去时。
您将处理的第二种类型的内存块是每行的存储。那里没有什么神秘的。您将为每个字符(空终止字符)分配存储空间+1,并将该行从固定缓冲区复制到分配的块。
两个内存块一起工作,因为要创建集合,您只需将保存数据行的内存块的地址分配给下一个可用指针。
让我们考虑一个简短的示例,其中我们将其声明char **lines;为指向保存指针的内存块的指针。然后假设我们最初分配两个指针,我们有可用的有效指针lines[0]和lines[1]。我们跟踪可用的指针数量nptrs和使用的指针数量used。所以最初nptrs = 2;和used = 0;.
当我们用 读取第一行时fgets(),我们将从字符串的末尾修剪掉'\n',然后得到字符串的长度 ( len = strlen(buffer);)。然后我们可以为字符串分配存储空间,将分配块的地址分配给我们的第一个指针,例如
lines[used] = malloc (len + 1);
Run Code Online (Sandbox Code Playgroud)
然后将内容复制buffer到lines[0],例如
memcpy (lines[used], buffer, len + 1);
Run Code Online (Sandbox Code Playgroud)
(注意:没有理由strcpy()再次调用并让它扫描字符串结尾,我们已经知道要复制多少个字符——包括nul 终止字符)
最后,让我们的计数器满意所需要的就是加used一。我们以相同的方式存储下一行,并在第三次迭代中used == nptrs存储更多指针(通常每次需要realloc()a 时将指针数量加倍)。realloc()这是对指针数量的调用和增长之间的良好平衡realloc()- 但您可以自由地以任何您喜欢的方式增加分配 - 但避免调用realloc()每一行。
因此,您不断读取行,检查是否realloc()需要,如果需要则重新分配,并为每行分配依次将起始地址分配给每个指针。唯一需要注意的是,当您realloc()始终使用临时指针时,当realloc()失败并返回时NULL,您不会覆盖原始指针,从而NULL丢失保存指针的内存块的起始地址,从而导致内存泄漏。
执行
概述中省略了详细信息,因此让我们看一个简短的示例,从文件中读取未知数量的行(每行为字符或更少),并使用指针到指针将每1024行存储在集合中如上所述。不要在代码中使用Magic-Numbers :char
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXC 1024 /* if you need a constant, #define one (or more) */
#define NPTRS 2 /* initial no. of pointers to allocate (lines) */
Run Code Online (Sandbox Code Playgroud)
也不要在代码中对文件名进行硬编码,argc这argv在int main (int argc, char **argv). 将要读取的文件名作为程序的第一个参数传递给程序(stdin如果没有给出参数,则默认读取):
int main (int argc, char **argv) {
char buf[MAXC], /* fixed buffer to read each line */
**lines = NULL; /* pointer to pointer to hold collection of lines */
size_t nptrs = NPTRS, /* number of pointers available */
used = 0; /* number of pointers used */
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
Run Code Online (Sandbox Code Playgroud)
(注意:您不需要仅仅为了读取不同的文件名而重新编译程序)
现在分配并验证您的初始指针数量
/* allocate/validate block holding initial nptrs pointers */
if ((lines = malloc (nptrs * sizeof *lines)) == NULL) {
perror ("malloc-lines");
exit (EXIT_FAILURE);
}
Run Code Online (Sandbox Code Playgroud)
读取每一行并从末尾修剪并获取删除'\n'后剩余的字符数(您可以使用一次完成所有操作):'\n'strcspn()
while (fgets (buf, MAXC, fp)) { /* read each line into buf */
size_t len;
buf[(len = strcspn (buf, "\n"))] = 0; /* trim \n, save length */
Run Code Online (Sandbox Code Playgroud)
接下来我们检查是否需要重新分配,如果需要则使用临时指针重新分配:
if (used == nptrs) { /* check if realloc of lines needed */
/* always realloc using temporary pointer (doubling no. of pointers) */
void *tmp = realloc (lines, (2 * nptrs) * sizeof *lines);
if (!tmp) { /* validate reallocation */
perror ("realloc-lines");
break; /* don't exit, lines still good */
}
lines = tmp; /* assign reallocated block to lines */
nptrs *= 2; /* update no. of pointers allocatd */
/* (optionally) zero all newly allocated memory here */
}
Run Code Online (Sandbox Code Playgroud)
现在分配并验证该行的存储,并将该行复制到新存储,used完成后递增 - 完成读取循环。
/* allocate/validate storage for line */
if (!(lines[used] = malloc (len + 1))) {
perror ("malloc-lines[used]");
break;
}
memcpy (lines[used], buf, len + 1); /* copy line from buf to lines[used] */
used += 1; /* increment used pointer count */
}
/* (optionally) realloc to 'used' pointers to size no. of pointers exactly here */
if (fp != stdin) /* close file if not stdin */
fclose (fp);
Run Code Online (Sandbox Code Playgroud)
现在,您可以根据程序中的需要使用存储的行lines,记住在完成后释放每行的内存,然后最后释放指针块,例如
/* use lines as needed (simply outputting here) */
for (size_t i = 0; i < used; i++) {
printf ("line[%3zu] : %s\n", i, lines[i]);
free (lines[i]); /* free line storage when done */
}
free (lines); /* free pointers when done */
}
Run Code Online (Sandbox Code Playgroud)
这就是所需要的一切。/usr/share/dict/words现在您可以在(或者可能在您的系统上,具体取决于发行版)阅读 324,000 个单词/var/lib/dict/words,这样做不会有任何问题。
输入文件
一个简短的示例文件:
lines[used] = malloc (len + 1);
Run Code Online (Sandbox Code Playgroud)
使用/输出示例
memcpy (lines[used], buffer, len + 1);
Run Code Online (Sandbox Code Playgroud)
内存使用/错误检查
在您编写的动态分配内存的任何代码中,对于分配的任何内存块,您有 2 个责任:(1) 始终保留指向内存块起始地址的指针,以便 (2) 可以释放它,(2)当内存块不可用时更需要。
您必须使用内存错误检查程序来确保您不会尝试访问内存或在分配的块的范围之外进行写入,尝试读取未初始化的值或将条件跳转建立在未初始化的值上,最后确认释放所有已分配的内存。
对于Linux来说valgrind是正常的选择。每个平台都有类似的内存检查器。它们使用起来都很简单,只需通过它运行您的程序即可。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXC 1024 /* if you need a constant, #define one (or more) */
#define NPTRS 2 /* initial no. of pointers to allocate (lines) */
Run Code Online (Sandbox Code Playgroud)
始终确认您已释放已分配的所有内存并且不存在内存错误。
完整代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXC 1024 /* if you need a constant, #define one (or more) */
#define NPTRS 2 /* initial no. of pointers to allocate (lines) */
int main (int argc, char **argv) {
char buf[MAXC], /* fixed buffer to read each line */
**lines = NULL; /* pointer to pointer to hold collection of lines */
size_t nptrs = NPTRS, /* number of pointers available */
used = 0; /* number of pointers used */
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
/* allocate/validate block holding initial nptrs pointers */
if ((lines = malloc (nptrs * sizeof *lines)) == NULL) {
perror ("malloc-lines");
exit (EXIT_FAILURE);
}
while (fgets (buf, MAXC, fp)) { /* read each line into buf */
size_t len;
buf[(len = strcspn (buf, "\n"))] = 0; /* trim \n, save length */
if (used == nptrs) { /* check if realloc of lines needed */
/* always realloc using temporary pointer (doubling no. of pointers) */
void *tmp = realloc (lines, (2 * nptrs) * sizeof *lines);
if (!tmp) { /* validate reallocation */
perror ("realloc-lines");
break; /* don't exit, lines still good */
}
lines = tmp; /* assign reallocated block to lines */
nptrs *= 2; /* update no. of pointers allocatd */
/* (optionally) zero all newly allocated memory here */
}
/* allocate/validate storage for line */
if (!(lines[used] = malloc (len + 1))) {
perror ("malloc-lines[used]");
break;
}
memcpy (lines[used], buf, len + 1); /* copy line from buf to lines[used] */
used += 1; /* increment used pointer count */
}
/* (optionally) realloc to 'used' pointers to size no. of pointers exactly here */
if (fp != stdin) /* close file if not stdin */
fclose (fp);
/* use lines as needed (simply outputting here) */
for (size_t i = 0; i < used; i++) {
printf ("line[%3zu] : %s\n", i, lines[i]);
free (lines[i]); /* free line storage when done */
}
free (lines); /* free pointers when done */
}
Run Code Online (Sandbox Code Playgroud)
仔细检查一下,如果有任何问题请告诉我。如果您还想读取未知长度的行(数百万个字符长),您只需循环执行相同的操作,为每一行分配和重新分配,直到'\n'找到EOF标记该行末尾的字符(或 )。原则上它与我们上面为指针所做的没有什么不同。