c/c++语言开发共享如何从文件中获取替代行并将其作为字符串存储到结构中?

我有一个文件需要通过代码读取。 该文件如下所示。 文件的第一行包含一个整数,表示文件中的日记条目数。 我需要编写一个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. 

我能够strtok()将日期,月份和年份存储在我的结构中,但是我坚持将字符串保存到我的结构中。 这是我的strtok()代码,

 FILE* file=fopen("struct.txt","r"); if (file==NULL){ perror("Error opening filen.");} else { fscanf(file,"%d",&size); res=(Diary*)malloc(size*sizeof(Diary)); fscanf(file,"%*[^n]"); while(fgets(day,1024,file)!= NULL){ oken=strtok(day,"/"); h[i]=atoi(oken); */h[i] is my day oken=strtok(NULL,"/"); fre[i]=atoi(oken); */fre[i] is the month oken=strtok(NULL,"/"); re[i]=atoi(oken); */ re[i] is the year okena=strtok(day,"n"); strcpy(rese[i],okena); */i had declared rese as char rese[1024] printf("%s",okena); i++; } 

程序没有使用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  #include  #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 failedm" ); 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++; } } 

    你的问题提出了一个经典的问题:“如果我事先不知道有多少东西,我如何阅读和分配X数?” 这实际上是问题的一个更简单的变体,因为您可以将X数字作为数据文件的第一行。

    (这会在读取第一行后将问题简化为单个X结构分配 – 否则您需要跟踪当前分配的结构数并根据需要重新分配)

    首先,我建议不要创建char entry[1024]; 在你的结构中有两个原因 – 首先,在堆栈上创建entry的自动存储,并且一个大的日记可以很容易地StackOverflow ……其次,它只是浪费。 如果目标是动态分配,则仅分配每个entry所需的存储。 您可以声明1024字符的单个缓冲区用作读缓冲区,但是然后仅分配strlen (buf) + 1 char来保存条目(在从条目中修剪包含的'n' )。

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

    将这些部分组合在一起,并在下面的评论中提供更多内容,您可以执行以下操作:

     #include  #include  #include  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 - %sn", 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; } 

    注意:您可以通过使用POSIX getline()来进一步简化代码,而不是使用固定的buf ,您可以使用strdup()简化每个条目的分配和复制到结构中,但不保证所有编译器都可以使用- 如果您的编译器支持它们并且无处不在,那么使用它们。另请注意,GNU gcc使用%zu作为size_t的格式说明符。如果您使用的是windoze,请将每个更改为%lu

    示例输入文件

     $ 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. 

    示例使用/输出

     $ ./bin/diary  

    内存使用/错误检查

    在您编写的动态分配内存的任何代码中,您对分配的任何内存块都有2个职责 :(1) 始终保留指向内存块起始地址的指针,因此,(2)当它为no时可以释放它需要更久。

    您必须使用内存错误检查程序,以确保您不会尝试访问内存或写入超出/超出已分配块的范围,尝试读取或基于未初始化值的条件跳转,最后,确认你释放了你分配的所有内存。

    对于Linux, valgrind是正常的选择。 每个平台都有类似的内存检查器。 它们都很简单易用,只需通过它运行程序即可。

     $ valgrind ./bin/diary  

    注意:所有日记条目(整个日记)所需的存储空间仅为309-bytes ,小于声明char entry[1024];所需存储空间的1/10th char entry[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. 

     > cl /nologo /Wall /wd4706 /wd4996 /Ox /Foobj/diary /Febin/diary /Tc diary.c 

    注意:我将.obj文件放在./obj中的子目录和我的二进制可执行文件中以保持我的源目录清洁。这就是上面的/Foobj/diary/Febin/diary的目的)

    示例使用/输出

     > bindiary.exe datdiary.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上无法使用。

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

      以上就是c/c++开发分享如何从文件中获取替代行并将其作为字符串存储到结构中?相关内容,想了解更多C/C++开发(异常处理)及C/C++游戏开发关注计算机技术网(www.ctvol.com)!)。

      本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。

      ctvol管理联系方式QQ:251552304

      本文章地址:https://www.ctvol.com/c-cdevelopment/522296.html

      (0)
      上一篇 2020年12月9日
      下一篇 2020年12月9日

      精彩推荐