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

4
我正在尝试使用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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#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;
}

1
strtok会返回原始字符串的一部分指针。当调用strtok时,原始字符串将被修改。 - nhahtdh
4个回答

5

这段代码存在几个问题。我已经快速修改了您的代码以达到预期的目的。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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;
}

已经有人对这个改变的细节进行了评论。


如果其他答案被删除,这可能并不有用。您介意添加一些细节吗? - S.S. Anne

2

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

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

只需在所有地方修改您对 value 的赋值:

result[i][0] = value;

To:

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

1
strdup不是标准的C函数。虽然可以实现:https://dev59.com/sHVC5IYBdhLWcg3wjx_u - nhahtdh

2

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

1

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

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

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

引用自文档

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

该函数会自动用空字符替换令牌的结束,并返回令牌的开始

并且(我强调):

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

delimiters:
包含分隔符的C字符串。
这些可能因每次调用而异。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接