使用fgets()和strtok()在C中逐行读取文件?

我正在尝试使用fgets和strtok()逐行读取文件,并创建每个不同信息行的链接列表。

现在,我只是将信息放入一个数组,只是为了试图弄清楚如何正确读取信息,但它不能正常工作。

在while(fgets)部分,它似乎正确地将所有内容加载到数组中,并将其打印出来。 然而,在循环执行之后我尝试打印整个数组,我得到了非常奇怪的结果..这主要是最后一行的部分,而不是大部分的完整单词或任何东西。

例如,如果我正在读书:

Simpson, Homer, Male, 1976 Simpson, Marge, Female, 1978 Simpson, Bart, Male, 2002 Simpson, Lisa, Female, 2004 Simpson, Maggie, Female, 2011 

我最后得到的打印输出是这样的:

 le Simpson Maggie Simpson Maggie e ale Simpson Maggie e e Simpson Maggie Female 2011 

请让我知道我哪里出错了,谢谢!

 #include  #include  #include  #define MAXSTRINGSIZE 10 #define LINESIZE 128 struct person{ char firstName[MAXSTRINGSIZE]; char lastName[MAXSTRINGSIZE]; char gender[MAXSTRINGSIZE]; int birthYear; struct person *next; } *first, *current; int main (void){ static const char filename[] = "Assignment1file.txt"; FILE *myfile = fopen ( "Assignment1file.txt", "r" ); int i=0; int j=0; int k=0; int l=0; char *result[10][4]; char line[LINESIZE]; char *value; for(i=0; i<9; i++){ for(j=0;j<4;j++){ result[i][j] = NULL; } } i=0; // loop through each entry in Assignment1file while(fgets(line, sizeof(line), myfile)){ //load last name value = strtok(line, ","); result[i][0] = value; printf("%i 0 %s", i, value); //load first time value = strtok(NULL, ","); result[i][1] = value; printf("%i 1 %s", i, value); // load gender value = strtok(NULL, ","); result[i][2] = value; printf("%i 2 %s", i, value); // load birth year value = strtok(NULL, "\n"); result[i][3] = value; printf("%i 3 %s", i, value); printf("\n"); for(j=0;j<4;j++){ printf("%s\n", result[i][j]); } //go to next line i++; } // read out the array for(k=0; k<5; k++){ for(j=0;j<4;j++){ printf("%s\n", result[k][j]); } } fclose(myfile); return 0; } 

strtok()返回line[]内的指针,因此当您读取下一行时,您保存的所有指针现在都指向存储文件最后一行的位置。

您可以为每个字符串分配内存,如下所示:

 //load last name value = strtok(line, ","); result[i][0] = malloc(strlen(value) + 1); strcpy(result[i][0], value); 

顺便说一句,你不需要在开始时将循环设置为NULL ,你可以这样做:

 char *result[10][4] = {0}; 

这段代码有几个问题。 我已经快速修改了你的代码以完成预期的工作。

 #include  #include  #include  #define MAXSTRINGSIZE 10 #define LINESIZE 128 struct person{ char firstName[MAXSTRINGSIZE]; char lastName[MAXSTRINGSIZE]; char gender[MAXSTRINGSIZE]; int birthYear; struct person *next; } *first, *current; int main (void){ FILE *myfile = fopen ( "Assignment1file.txt", "r" ); int i=0; int j=0; int k=0; int l=0; char *result[10][4]; char line[LINESIZE]; char *value; for(i=0; i<=9; i++){ for(j=0;j<=4;j++){ result[i][j] = NULL; } } i=0; // loop through each entry in Assignment1file while(fgets(line, sizeof(line), myfile)){ //load last name value = strtok(line, ", "); result[i][0] = strdup(value); printf("last: %s\n", value); //load first time value = strtok(NULL, ", "); result[i][1] = strdup(value); printf("first: %s\n", value); // load gender value = strtok(NULL, ", "); result[i][2] = strdup(value); printf("gender: %s\n", value); // load birth year value = strtok(NULL, " \n"); result[i][3] = strdup(value); printf("birth year: %s\n", value); //go to next line i++; } // read out the array for(k=0; k<5; k++){ for(j=0;j<4;j++){ printf("%s\n", result[k][j]); } } fclose(myfile); return 0; } 

人们已经对这一变化的细节进行了评论。

strtok将修改原始字符串。 因此,在每次迭代后,您存储的先前指针不再存在。

简单的解决方案是使用: strdup来分配和复制值。

只需在任何地方修改您的value分配:

 result[i][0] = value; 

至:

 result[i][2] = strdup(value); 

如果需要,您需要将令牌复制到单独的存储中。

strtok()将修改读取行的缓冲区,并用NUL字符替换分隔符,并返回指向缓冲区中某个位置的指针(即当前标记的开头)。

当您在下一行中读取时,缓冲区将填充新数据,因此,您保存的所有指针都是无用的,因为之前的数据现在已经消失。

从文档引用:

为了确定令牌的开始和结束,该函数首先从起始位置扫描包含在分隔符中的第一个字符(它成为令牌开头 )。 然后从令牌的这个开头开始扫描包含在分隔符中的第一个字符,这将成为令牌结尾

令牌的这一端由函数自动替换为空字符,并且该函数返回令牌开头

并且(强调我的):

海峡
C字符串要截断。 修改此字符串的内容并将其分解为更小的字符串(标记)。
或者,可以指定空指针,在这种情况下,该function继续扫描先前成功调用该函数的位置。

分隔符:
包含分隔符的C字符串。
这些可能因呼叫而异。