我怀疑您的课程涉及递归,因为story 数组中的每个元素都需要分支未知次数来读取file_x 和file_y(每个都可以包含额外的file_x和file_y 内)。您的程序选项是跟踪所有file_x,然后返回到每个file_y,重复该过程,直到到达file_x 和file_y 为空的每个链中的最终文件。
在确定您将采用哪种方法之前,您只需要一种方法来读取一个文件,提取title、file_x、file_y 并分配和存储text。这是一个相当简单的过程,您的主要任务是验证每个步骤,以便您有信心处理实际数据并且不通过读取实际上未打开的文件或尝试写入(或读取)超出存储范围的文件来调用未定义的行为。
这是一个简短的示例,它使用指向 story 的指针来填充并从 filename 中读取。您会注意到涉及的重复过程。 (使用fgets 读取字符串,获取长度,验证最后读取的字符是'\n',表示您读取了整行,最后通过使用nul-terminating覆盖来修剪'\n' > 字符,这样您就不会在存储的字符串的末尾悬挂换行符,或者在将行连接在一起的text 的情况下用' '(空格)覆盖。
注意:在下面,realloc 永远不会直接在指向 text 的指针上调用。相反,tmp 指针与realloc 一起使用,以在将新块分配给text 之前验证realloc 是否成功。 (否则,如果realloc 失败,您将丢失指向text 的指针——因为它返回NULL)
/* read values into struct story 's' from 'filename' */
int read_file (story *s, char *filename)
{
size_t len = 0, /* var for strlen */
text_size = 0, /* total text_size */
nul_char = 0; /* flag for +1 on first allocation */
char buf[TITLE_MAX] = ""; /* read buffer for 'text' */
FILE *fp = fopen (filename, "r"); /* file pointer */
if (!fp) /* validate file open for reading */
return 0; /* or return silently indicating no file_x or file_y */
if (fgets (s->title, TITLE_MAX, fp) == 0) { /* read title */
fprintf (stderr, "error: failed to read title from '%s'.\n",
filename);
fclose(fp);
return 0;
}
len = strlen (s->title); /* get title length */
if (len && s->title[len - 1] == '\n') /* check last char is '\n' */
s->title[--len] = 0; /* overwrite with nul-character */
else { /* handle error if line too long */
fprintf (stderr, "error: title too long, filename '%s'.\n",
filename);
fclose(fp);
return 0;
}
if (fgets (s->file_x, PATH_MAX, fp) == 0) { /* same for file_x */
fprintf (stderr, "error: failed to read file_x from '%s'.\n",
filename);
fclose(fp);
return 0;
}
len = strlen (s->file_x);
if (len && s->file_x[len - 1] == '\n')
s->file_x[--len] = 0;
else {
fprintf (stderr, "error: file_x too long, filename '%s'.\n",
filename);
fclose(fp);
return 0;
}
if (fgets (s->file_y, PATH_MAX, fp) == 0) { /* same for file_y */
fprintf (stderr, "error: failed to read file_y from '%s'.\n",
filename);
fclose(fp);
return 0;
}
len = strlen (s->file_y);
if (len && s->file_y[len - 1] == '\n')
s->file_y[--len] = 0;
else {
fprintf (stderr, "error: file_y too long, filename '%s'.\n",
filename);
fclose(fp);
return 1;
}
while (fgets (buf, TITLE_MAX, fp)) { /* read text in TITLE_MAX chunks */
len = strlen (buf);
if (len && buf[len - 1] == '\n') /* check for '\n' */
buf[len - 1] = ' '; /* overwrite with ' ' for concat */
if (text_size == 0)
nul_char = 1; /* account for space for '\0' when empty, and */
else /* use a flag to set new block to empty-string */
nul_char = 0;
void *tmp = realloc (s->text, text_size + len + nul_char); /* allocate */
if (!tmp) { /* validate realloc succeeded */
fprintf (stderr, "error: realloc failed, filename '%s'.\n",
filename);
break;
}
s->text = tmp; /* assign new block to s->text */
if (nul_char) /* if first concatenation */
*(s)->text = 0; /* initialize s->text to empty-string */
strcat (s->text, buf); /* concatenate buf with s->text */
text_size += (len + 1); /* update text_size total */
}
fclose (fp); /* close file */
return 1;
}
有了这个,您将需要设计一种方法来处理所有file_x 和file_y 文件名。如上所述,这可能适用于递归函数,或者您可以沿着file_x 树向下工作并返回并拾取所有file_y 添加的内容。请注意,每次关注file_x 或file_y 时,您都需要考虑新添加的story。
下面是一个简短的示例,它遵循所有file_x 添加并返回并遵循仅第一个 file_y 分支。它旨在向您展示如何处理来自file_x 和file_y 的调用和填充,而不是为您编写最终代码。如果您在read_file 函数上方添加以下内容,您将获得一个工作示例:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h> /* for PATH_MAX */
enum { STORY_MAX = 12, TITLE_MAX = 1024 };
typedef struct
{
char title[TITLE_MAX],
file_x[PATH_MAX],
file_y[PATH_MAX],
*text;
} story;
int read_file (story *s, char *filename);
int main (int argc, char **argv) {
int n = 0, storycnt = 0;
story stories[STORY_MAX] = {{ .title = "" }};
char *filename = argv[1];
/* read all file_x filenames */
while (n < STORY_MAX && read_file (&stories[n], filename)) {
filename = stories[n++].file_x;
}
storycnt = n; /* current story count of all file_x */
for (int i = 0; i < storycnt; i++) /* find all file_y files */
while (n < STORY_MAX && read_file (&stories[n], stories[i].file_y)) {
filename = stories[i++].file_y;
n++;
}
for (int i = 0; i < n; i++) { /* output stories content */
printf ("\ntitle : %s\nfile_x: %s\nfile_y: %s\ntext : %s\n",
stories[i].title, stories[i].file_x,
stories[i].file_y, stories[i].text);
free (stories[i].text); /* don't forget to free memory */
}
return 0;
}
输入文件示例
$ cat file_1.txt
File 1
file_8.txt
file_25.txt
Text: "this is some example text, lenght is unknown
so i will have to use malloc and realloc to
dynamicaly store it."
$ cat file_8.txt
file_8
This is the text from file 8. Not much,
just some text.
$ cat file_25.txt
file_25
This is the text from file 25. Not much,
just some text.
使用/输出示例
$ ./bin/rdstories file_1.txt
title : File 1
file_x: file_8.txt
file_y: file_25.txt
text : Text: "this is some example text, lenght is unknown so i will
have to use malloc and realloc to dynamicaly store it."
title : file_8
file_x:
file_y:
text : This is the text from file 8. Not much, just some text.
title : file_25
file_x:
file_y:
text : This is the text from file 25. Not much, just some text.
内存使用/错误检查
在您编写的任何动态分配内存的代码中,对于分配的任何内存块,您都有 2 个职责:(1)始终保留指向起始地址的指针内存块,因此 (2) 当不再需要它时可以释放。
对于 Linux,valgrind 是正常的选择。每个平台都有类似的内存检查器。它们都易于使用,只需通过它运行您的程序即可。
$ valgrind ./bin/rdstories file_1.txt
==9488== Memcheck, a memory error detector
==9488== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==9488== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==9488== Command: ./bin/rdstories file_1.txt
==9488==
title : File 1
file_x: file_8.txt
file_y: file_25.txt
text : Text: "this is some example text, lenght is unknown so i will
have to use malloc and realloc to dynamicaly store it."
title : file_8
file_x:
file_y:
text : This is the text from file 8. Not much, just some text.
title : file_25
file_x:
file_y:
text : This is the text from file 25. Not much, just some text.
==9488==
==9488== HEAP SUMMARY:
==9488== in use at exit: 0 bytes in 0 blocks
==9488== total heap usage: 13 allocs, 13 frees, 3,353 bytes allocated
==9488==
==9488== All heap blocks were freed -- no leaks are possible
==9488==
==9488== For counts of detected and suppressed errors, rerun with: -v
==9488== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
始终确认您已释放已分配的所有内存并且没有内存错误。
检查一下,如果您有任何问题,请告诉我。