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

标签 c file-io

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

[Title of file]
[name of file X]
[name of file Y]
[Text]

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

文件 1
file_8.txt
file_25.txt
文本:“这是一些示例文本,长度未知,所以
所以我必须使用 malloc 和 realloc 来
动态存储它。”


第一个文件的名称由用户在启动程序时在标准输入中键入
(示例:./task1 page_1.txt)

The first line stores the title of the file.
The second and third lines each contain a file name of a next file that i have to read/store.If there are no further names of files on 2nd and 3rd line, both of the lines will have " -\n ".
Text starts at fourth line (can have multiple lines like in example above)

我现在的结构:

#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;

我的主要看起来像这样:

int main (int argc,char *argv[])
{
    char c[100];
    char buffer[100];
    FILE *input = fopen(argv[1], "r");
    Story *temp = (Story*) malloc(sizeof(Story) * 8);
if(input)
{
    int flag = 0;
    while (fgets(c, sizeof(buffer),input) != NULL)      
    {
        if(flag == 0)
        {
            sscanf(c, "%s", temp->title);
        }       
        else if(flag == 1)
        {
            sscanf(c, "%s", temp->file_x);
        }
        else if(flag == 2)
        {
            sscanf(c, "%s", temp->file_y);
        }
        else
        {
            while(!feof(input))
            {
                fread(temp->text, sizeof(Story),1,input);
            }
        }
        flag++;
    }
    printf("%s\n%s\n%s\n", temp->title,
    temp->file_x, temp->file_y);
}
else if (input == NULL)
{
    printf("ERROR MESSAGE HERE \n");
    return 1;
}
free(temp);
fclose(input);
return 0;

}

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

最佳答案

我怀疑你的类(class)涵盖了递归,就像 story 数组中的每个元素一样。您将需要分支未知次数才能读取 file_xfile_y (其中每个都可以包含额外的 file_xfile_y)。你的程序选择是追踪所有file_x的踪迹。然后返回每个file_y重复该过程,直到到达每个链中的最终文件,其中 file_xfile_y都是空的。

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

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

注意:如下,realloc永远不会直接在指向 text 的指针上调用。相反,tmp指针与 realloc 一起使用验证realloc在将新 block 分配给 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);
        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_xfile_y文件名。如上所述,这可能适合递归函数,或者您可以沿着 file_x 继续下去。树然后绕回来并捡起所有file_y补充。请注意,您需要考虑新添加的 story每次 file_xfile_y已关注。

下面是一个简短的示例,贯穿所有 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.

内存使用/错误检查

在您编写的动态分配内存的任何代码中,对于分配的任何内存块,您都有两个责任:(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)

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

仔细检查一下,如果有疑问请告诉我。

关于c - 打开多个文件并将它们存储在结构数组中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47563405/

相关文章:

c - 在头文件中使用 "EXTERN"关键字,而其定义在源文件中

c - C 中读取超出对象未定义行为吗?

c# - 锁定文件以进行写入/删除,同时允许任何进程读取

java - 如何读取巨大压缩文件的最后 n 行而不将整个文件解压到磁盘

java - 当 File.listFiles 返回 null 时检索底层错误

c - 用于理解指针算术的 OpenCV 视觉效果

c - 循环中的表无法正确显示数据

c - 将文本文件读入链表

c++ - QML - 如何在 ListView 上显示文本文件?

c++ - 如何通过跳过=从txt文件中读取整数?