打开多个文件并将它们存储在结构数组中

时间:2017-11-29 23:10:47

标签: c file-io

我的任务需要一些帮助。我必须打开未知数量的文件并将它们存储在程序开始的数据结构中。
第一个文件包含其他两个文件的名称等等(这在更多的例子中进行了解释)第一个文件)。 每个文件具有相同的结构:

  

[文件名]
[文件名X]
[文件名Y]
[文本]

,对于示例第一个文件将如下所示:

File 1
file_8.txt
file_25.txt
Text: "this is some example text, lenght is unknown so
so i will have to use malloc and realloc to
{{{ 1}}


在启动程序时,用户在stdin中键入第一个文件的名称
(例如:dynamicaly store it."

  

第一行存储文件的标题。
第二行和第三行各包含一个我必须读/存的下一个文件的文件名。如果第二行和第三行没有其他文件名,则两行都有" - \ n "。
文字从第四行开始(可以有多行,如上例所示)

我的结构目前:

./task1 page_1.txt

我的主要看起来像这样:

#include <stdio.h>
#include <stdlib.h>

typedef struct 
{
    char title[1000];    // should use malloc and realloc and not this way
    char file_x[1000];  // dynamically
    char file_y[1000];  // dynamically
    char text[10000]; 

} Story;

}

现在我设法打开第一个文件并将其存储到结构中。我需要知道如何打开和存储所有其他文件,还必须使用动态内存分配来实现它。
非常感谢任何建议。

1 个答案:

答案 0 :(得分:0)

我怀疑你的课程是覆盖递归,就像你的story数组中的每个元素一样,你需要分支未知的次数来阅读file_xfile_y(每个元素)可以包含其他file_xfile_y。您的程序选项是跟踪所有file_x的跟踪,然后返回到重复此过程的每个file_y,直到您到达file_xfile_y的每个链中的最终文件为止是空的。

在确定您将采用哪种方法之前,您只需要一种方法来阅读一个文件,提取titlefile_xfile_y并分配和存储text。这是一个相当简单的过程,您的主要任务是 验证每个步骤 ,这样您就可以确信您正在处理实际数据并且不要通过读取实际未打开或尝试写入(或读取)超出存储范围的文件来调用 Undefined Behavior

这是一个简短的示例,它指向要填充的story和要读取的filename。您将注意到涉及的重复过程。 (读取带有fgets的字符串,获取长度,验证最后一个char读取是'\n',表明你读了整行,最后通过用覆盖来修剪'\n' nul-terminated 字符,这样您就不会在存储的字符串末尾悬挂换行符,或者在' '的情况下使用text(空格)覆盖新的行串联起来。

注意:下面,永远不会在指向realloc的指针上调用text。相反,tmp指针与realloc一起使用,以在将新块分配给realloc之前验证text成功。 (否则如果text失败,您将失去指向realloc的指针 - 因为它返回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);
        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);
        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);
        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);
        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);
        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);
        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_xfile_y文件名。如上所述,这可能适用于递归函数,或者您可以沿着file_x树向下工作并回过头来获取所有file_y个添加项。请注意,每次跟踪storyfile_x时,您都需要考虑新添加的file_y

下面是一个简短的示例,其中包含所有file_x个添加内容,然后返回并执行仅第一个 file_y分支。它旨在向您展示如何处理来自file_xfile_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)

始终确认已释放已分配的所有内存并且没有内存错误。

仔细看看,如果您有疑问,请告诉我。