C语言 fscanf to a linked list



我的代码又遇到了麻烦。
我想fscanf结果.txt到带有链表的结构,但它不起作用;我认为简单的链表一定足够了;

问题是:程序只写第一行,但没有别的。

结果.txt格式:

point name (for examples)
623   john
457   peter
312   chuck
etc.

代码:

    #include <stdio.h>
    #include <stdlib.h>
    typedef struct ranklist {
        int point;
        char* name;
        struct ranklist *next;
    } ranklist;
    int how_many_records(FILE *fp){
        char ch;
        int line=0;
        int status;
        rewind(fp);
        while((status=fscanf(fp, "%*d %*[^n]%c", &ch))==1)
            ++line;
        if(status != EOF){
            ++line;
        }
        rewind(fp);
        return line;
    }
    int how_many_letter(FILE *fp){
        int letter = 0;
        long pos = ftell(fp);
        //fscanf(fp, " %*[^n]%n", &letter);
        fscanf(fp, " %*s%n", &letter);
        fseek(fp, pos, SEEK_SET);
        return letter;
    }
    int main(void){
        FILE *fp = fopen("result.txt","r");
        int name_length;
        int lines = how_many_records(fp);
        ranklist *r = malloc(lines * sizeof(*r));
        ranklist *first = r;

        for ( r=first  ;r != NULL; r = r->next){
            fscanf(fp, "%d", &(r->point));
            name_length = how_many_letter(fp);
            r->name = malloc(name_length + 1);
            fscanf(fp,"%s", r->name);
        }
        fclose(fp);
        for ( r=first  ;r != NULL; r = r->next){
            printf("%d %sn", r->point, r->name);
        }
        free(r);
        return 0;
    }

fscanf(fp, "%d", &r[y].point);

在这里,y是未初始化的。

你需要把y = 0,或者,IMO,更好地使用&(r->point)[r[y].name也是如此]

建议:用于读取和解析整行更好地使用fgets()

创建列表时遇到多个问题。

让我们从循环开始:

for ( r=first  ;r != NULL; r = r->next){

在循环中没有任何地方初始化r->next,所以在第一次迭代之后,你会y指向完全随机的内存,导致未定义的行为

如另一个答案所述,您不初始化变量y,这是未定义行为的另一个原因。

您还更改r,因此r不会指向您分配的原始内存,因此这样做r[y]是未定义行为的第三个原因。

在循环之后,你用修改后的指针y调用free,这也会导致未定义的行为。而且您不会释放在循环中分配的名称,因此也会有多个内存泄漏。


根本不需要预先分配节点,只需在需要时分配即可。

类似的东西

ranklist *head = NULL;
FILE *fp = fopen(...);
// Read the file and create the list
char buffer[256];
while (fgets(buffer, sizeof(buffer), fp) != NULL)
{
    ranklist *node = malloc(sizeof(ranklist));
    node->name = malloc(strlen(buffer));  // Will allocate a little more than needed
    sscanf(buffer, "%d %s", &node->point, node->name);
    node->next = head;
    head = node;
}
// Print the list
for (ranklist *n = head; n != NULL; n = n->next)
{
    printf("%d %sn", n->point, n->name);
}
// Free the list
while (head != NULL)
{
    // Unlink the first node in the list
    ranklist *n = head;
    head = n->next;
    // And free it
    free(n->name);
    free(n);
}

上面的代码没有任何错误检查,在为名称分配空间时也有一些开销,但它是安全的,它不会处理不切实际的长名称,并且列表实际上将是一个堆栈(最后读取的项目将是列表中的第一个(。


从用fgets读取的缓冲区中获取名称长度的函数:

size_t get_name_length(char *buffer)
{
    // Since the buffer was read with `fgets` we need to get rid
    // of the newline at the end, if it's there
    size_t length = strlen(buffer);
    if (buffer[length - 1] == 'n')
        buffer[length - 1] = '';  // "Remove" by terminating the string
    // Find the space dividing the point and the name
    char *space = strchr(buffer, ' ');
    // Just in case there are multiple whitespace characters in the string
    while (*space != '' && isspace(*space))
        ++space;
    // Now `space` points to the first non-space letter in the name
    // (or to the string terminator, if there's no name
    // Then length of the name is the remainder of the string
    return strlen(space);
}
FILE *fp = fopen("result.txt","r");
int name_length;
//Just create a link if you use as an array
//int lines = how_many_records(fp);
//ranklist *r = malloc(lines * sizeof(*r));
ranklist *first=NULL, *curr, *r;
int point;
while(1==fscanf(fp, "%d", &point)){
    r = malloc(sizeof(*r));//allocate one record
    r->next = NULL;
    r->point = point;
    name_length = how_many_letter(fp);
    r->name = malloc(name_length + 1);
    fscanf(fp, "%s", r->name);
    //make link
    if(first == NULL)
        first = curr = r;
    else
        curr = curr->next = r;
}
fclose(fp);
for (r = first; r != NULL; r = r->next){
    printf("%d %sn", r->point, r->name);
}
for (r = first; r != NULL; ){
    ranklist *tmp = r->next;//Save next for free(r)
    free(r->name);
    free(r);
    r = tmp;
}

//output the list
void print(ranklist *first){
    while(first != NULL){
        printf("%d %sn", first->point, first->name);
        first = first->next;
    }
}

最新更新