C 如何从文件中获取替代行并将其作为字符串存储到结构中?

C 如何从文件中获取替代行并将其作为字符串存储到结构中?,c,string,file,struct,io,C,String,File,Struct,Io,我有一个文件需要通过代码读取。该文件如下所示。文件的第一行包含一个整数,表示文件中日记账分录的数量。我需要编写一个C程序来读取文件并将内容存储在动态分配的结构数组中 4 12/04/2010 Interview went well I think, though was told to wear shoes. 18/04/2010 Doc advised me to concentrate on something... I forget. 03/05/2010 Was asked to

我有一个文件需要通过代码读取。该文件如下所示。文件的第一行包含一个整数,表示文件中日记账分录的数量。我需要编写一个C程序来读取文件并将内容存储在动态分配的结构数组中

4
12/04/2010 
Interview went well I think, though was told to wear shoes. 
18/04/2010 
Doc advised me to concentrate on something... I forget. 
03/05/2010
Was asked today if I was an art exhibit. 
19/05/2010 
Apparently mudcakes not made of mud, or angry wasps.
我能够在struct中存储strok()的日期、月份和年份,但是我一直在将字符串保存到struct中。 这是我的strtok()代码

程序没有使用strcpy(),当我运行它时,它不断崩溃。但是,如果删除strcpy(),它将按如下方式打印:

12
Interview went well I think, though was told to wear shoes. 
18
Doc advised me to concentrate on something... I forget. 
03
Was asked today if I was an art exhibit. 
19
Apparently mudcakes not made of mud, or angry wasps.
这不是我想要存储在结构中的字符串。我被困在如何将字符串存储到结构中。我的结构是

typedef struct journal{
int day;
int month;
int year;
char entry[1024];
} Diary;

任何善良的灵魂都能告诉我哪里不对吗?

以下建议代码:

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

#define MAX_LINE_LEN 1024

struct journal
{
    int day;
    int month;
    int year;
    char entry[ MAX_LINE_LEN ];
};
typedef struct journal Diary;


int main( void )
{

    FILE* file=fopen("struct.txt","r");
    if ( !file )
    {
        perror("fopen failed");}
        exit( EXIT_FAILURE );
    }

    // implied else, fopen successful

    char line[ MAX_LINE_LEN ];
    int size;

    if( fgets( line, sizeof line, file ) )
    {
        if ( sscanf( line, "%d", size ) != 1 )
        {
            fprintf( stderr, "scanf for data count failed\m" );
            exit( EXIT_FAILURE );
        }

        // implied else, input of data count successful
    }

    else
    {
        perror( "fgets for data count failed" );
        exit( EXIT_FAILURE );
    }

    // implied else, fgets successful


    Diary myDiary[ size ];  // uses VLA (variable length array feature of C


    size_t i = 0;
    char *token = NULL;

    while( i < size && fgets( line, sizeof( line ), file) )
    {
        token = strtok( line, "/" );
        if( token )
        {
            myDiary[i].day = atoi( token );

            token = strtok( NULL, "/" );
            if( token )
            {
                myDiary[i].month = atoi( token );

                token = strtok( NULL, "/" );
                if( token )
                {
                    myDiary[i].year = atoi( token );

                    // input data directly into struct instance
                    fgets( myDiary[i].entry, MAX_LINE_LEN, file );
                }
            }
        }
        i++;
    }
}
  • 执行所需的功能
  • 为“神奇”数字提供有意义的名称
  • 将结构定义与该结构的类型定义分开
  • 更新/编辑了最新的问题详细信息
  • 现在建议的守则是:

    #include <stdio.h>
    #include <stdlib.h>
    
    #define MAX_LINE_LEN 1024
    
    struct journal
    {
        int day;
        int month;
        int year;
        char entry[ MAX_LINE_LEN ];
    };
    typedef struct journal Diary;
    
    
    int main( void )
    {
    
        FILE* file=fopen("struct.txt","r");
        if ( !file )
        {
            perror("fopen failed");}
            exit( EXIT_FAILURE );
        }
    
        // implied else, fopen successful
    
        char line[ MAX_LINE_LEN ];
        int size;
    
        if( fgets( line, sizeof line, file ) )
        {
            if ( sscanf( line, "%d", size ) != 1 )
            {
                fprintf( stderr, "scanf for data count failed\m" );
                exit( EXIT_FAILURE );
            }
    
            // implied else, input of data count successful
        }
    
        else
        {
            perror( "fgets for data count failed" );
            exit( EXIT_FAILURE );
        }
    
        // implied else, fgets successful
    
    
        Diary myDiary[ size ];  // uses VLA (variable length array feature of C
    
    
        size_t i = 0;
        char *token = NULL;
    
        while( i < size && fgets( line, sizeof( line ), file) )
        {
            token = strtok( line, "/" );
            if( token )
            {
                myDiary[i].day = atoi( token );
    
                token = strtok( NULL, "/" );
                if( token )
                {
                    myDiary[i].month = atoi( token );
    
                    token = strtok( NULL, "/" );
                    if( token )
                    {
                        myDiary[i].year = atoi( token );
    
                        // input data directly into struct instance
                        fgets( myDiary[i].entry, MAX_LINE_LEN, file );
                    }
                }
            }
            i++;
        }
    }
    
    #包括
    #包括
    #定义最大线长度1024
    结构日志
    {
    国际日;
    整月;
    国际年;
    字符输入[最大行长度];
    };
    typedef结构日志;
    内部主(空)
    {
    FILE*FILE=fopen(“struct.txt”、“r”);
    如果(!文件)
    {
    perror(“fopen失败”);}
    退出(退出失败);
    }
    //否则,fopen成功了
    字符行[MAX_line_LEN];
    整数大小;
    if(fgets(行、行大小、文件))
    {
    如果(sscanf(行,“%d”,大小)!=1)
    {
    fprintf(stderr,“扫描数据计数失败\m”);
    退出(退出失败);
    }
    //否则,数据计数输入成功
    }
    其他的
    {
    perror(“数据计数的FGET失败”);
    退出(退出失败);
    }
    //否则,fgets将获得成功
    日记我的日记[大小];//使用VLA(C的可变长度数组功能
    尺寸i=0;
    char*token=NULL;
    而(i
    您的问题提出了一个经典问题:“当我事先不知道有多少时,如何读取和分配某物的X号?”这实际上是一个更简单的问题,因为您可以从数据文件中读取
    X
    号作为第一行

    (在阅读第一行之后,这将问题简化为单个X结构的分配-否则您需要根据需要跟踪当前分配的结构数量和
    realloc

    首先,我建议不要创建
    char条目[1024];
    在您的结构中,有两个原因-第一,在堆栈上创建了
    条目的自动存储,并且一个大的日志很容易堆栈溢出…第二,这只是浪费。如果目标是动态分配,那么只分配每个
    条目所需的存储。您可以声明一个
    1024的缓冲区
    char用作读取缓冲区,但随后仅分配
    strlen(buf)+1
    char来保存条目(从条目中修剪包含的
    '\n'
    后)

    问题的其余部分是任何可靠代码的基础,只是验证每次读取、每次解析和每次分配,以确保您处理的是有效数据,并在整个代码中拥有有效存储。这适用于您编写的每一段代码,而不仅仅是此问题

    将这些部分放在一起,并在下面的评论中提供进一步的详细信息,您可以执行以下操作:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    typedef struct journal {
        int day,
            month,
            year;
        char *entry;    /* declare a pointer, allocate only need No. of chars */
    } diary_t;
    
    #define MAXLENGTH 1024  /* max read buf for diary entry */
    
    int main (int argc, char **argv) {
    
        size_t entries = 0, i, n = 0;
        char buf[MAXLENGTH] = "";
        diary_t *diary = NULL;
        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;
        }
    
        /* read first line, parse number of entries */
        if (!(fgets (buf, MAXLENGTH, fp)) ||        /* validate read */
            sscanf (buf, "%zu", &entries) != 1) {   /* validate conversion */
            fputs ("error: failed to read 1st line.\n", stderr);
            return 1;
        }
    
        /* allocate/validate entries number of diary_t */
        if (!(diary = calloc (entries, sizeof *diary))) {
            perror ("calloc-diary_pointers");
            return 1;
        }
    
        for (i = 0; i < entries; i++) { /* loop No. entries times */
    
            size_t len = 0;
            if (!fgets (buf, MAXLENGTH, fp)) {  /* read/validate date */
                fprintf (stderr, "error: failed to read date %zu.\n", i);
                return 1;
            }
            if (sscanf (buf, "%d/%d/%d",    /* parse into day, month, year */
                        &diary[i].day, &diary[i].month, &diary[i].year) != 3) {
                fprintf (stderr, "error failed to parse date %zu.\n", i);
                return 1;
            }
    
            if (!fgets (buf, MAXLENGTH, fp)) {  /* read entry */
                fprintf (stderr, "error: failed to read entry %zu.\n", i);
                return 1;
            }
    
            len = strlen (buf);                 /* get length */
            if (len && buf[len - 1] == '\n')    /* check last char is '\n' */
                buf[--len] = 0;                 /* overwrite with nul-character */
            else if (len == MAXLENGTH - 1) {    /* check entry too long */
                fprintf (stderr, "error: entry %zu exceeds MAXLENGTH.\n", i);
                return 1;
            }
    
            /* allocate/validate memory for entry */
            if (!(diary[i].entry = malloc ((len + 1)))) {
                perror ("malloc-diary_entry");
                fprintf (stderr, "error: memory exausted, entry[%zu].\n", i);
                break;  /* out of memory error, don't exit, just break */
            }
            strcpy (diary[i].entry, buf);   /* copy buf to entry */
    
            n++;    /* increment successful entry read */
        }
        if (fp != stdin) fclose (fp);   /* close file if not stdin */
    
        for (i = 0; i < n; i++) {   /* output diary entries */
            printf ("entry[%2zu]:  %2d/%2d/%4d - %s\n", i, diary[i].day,
                    diary[i].month, diary[i].year, diary[i].entry);
            free (diary[i].entry);  /* don't forget to free entries */
        }
        free (diary);   /* don't forget to free diary */
    
        return 0;
    }
    
    示例使用/输出

    $ ./bin/diary <dat/diary.txt
    entry[ 0]:  12/ 4/2010 - Interview went well I think, though was told to wear shoes.
    entry[ 1]:  18/ 4/2010 - Doc advised me to concentrate on something... I forget.
    entry[ 2]:   3/ 5/2010 - Was asked today if I was an art exhibit.
    entry[ 3]:  19/ 5/2010 - Apparently mudcakes not made of mud, or angry wasps.
    
    > bin\diary.exe dat\diary.txt
    entry[ 0]:  12/ 4/2010 - Interview went well I think, though was told to wear shoes.
    entry[ 1]:  18/ 4/2010 - Doc advised me to concentrate on something... I forget.
    entry[ 2]:   3/ 5/2010 - Was asked today if I was an art exhibit.
    entry[ 3]:  19/ 5/2010 - Apparently mudcakes not made of mud, or angry wasps.
    
    注意:所有日记条目(整个日记)所需的存储空间仅为
    309字节
    ,小于
    1/10
    声明
    char条目所需的存储空间[1024];

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


    MS Windows

    由于您在windows上似乎遇到了问题,下面是上面的代码,除了用
    %lu
    代替
    %zu
    (因为windows将
    %zu
    视为一个文本)之外,没有其他内容,而是在Win7上使用旧版本的VS编译器编译的:

    > cl /?
    Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.30319.01 for 80x86
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    编译

    $ cat dat/diary.txt
    4
    12/04/2010
    Interview went well I think, though was told to wear shoes.
    18/04/2010
    Doc advised me to concentrate on something... I forget.
    03/05/2010
    Was asked today if I was an art exhibit.
    19/05/2010
    Apparently mudcakes not made of mud, or angry wasps.
    
    > cl /nologo /Wall /wd4706 /wd4996 /Ox /Foobj/diary /Febin/diary /Tc diary.c
    
    注意:我将.obj文件放在子目录
    /obj
    中,将二进制可执行文件放在
    /bin
    中,以保持源目录干净。这就是上面
    /Foobj/diary
    /Febin/diary
    的目的)

    示例使用/输出

    $ ./bin/diary <dat/diary.txt
    entry[ 0]:  12/ 4/2010 - Interview went well I think, though was told to wear shoes.
    entry[ 1]:  18/ 4/2010 - Doc advised me to concentrate on something... I forget.
    entry[ 2]:   3/ 5/2010 - Was asked today if I was an art exhibit.
    entry[ 3]:  19/ 5/2010 - Apparently mudcakes not made of mud, or angry wasps.
    
    > bin\diary.exe dat\diary.txt
    entry[ 0]:  12/ 4/2010 - Interview went well I think, though was told to wear shoes.
    entry[ 1]:  18/ 4/2010 - Doc advised me to concentrate on something... I forget.
    entry[ 2]:   3/ 5/2010 - Was asked today if I was an art exhibit.
    entry[ 3]:  19/ 5/2010 - Apparently mudcakes not made of mud, or angry wasps.
    
    您必须确保将每个
    %zu
    更改为
    %lu
    ,否则无法获得正确的输出。您说您已将所有更改为
    int
    ,但您在下面的评论中发布的代码段包含
    %zu
    ——这在windows上不起作用


    请仔细检查,如果您还有其他问题,请告诉我。

    有两个
    fgets
    呼叫吗?一个用于日期行,一个用于“输入”行。在
    h[i]=atoi中
    h
    是什么;
    ?请提供一个。如果我这样做,我应该将日期行和输入行存储到哪里?我尝试使用fgets作为日期和输入,然后继续使用另一个