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