【问题标题】:fscanf to a linked listfscanf 到链表
【发布时间】:2014-12-03 10:03:04
【问题描述】:

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

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

result.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 %s\n", r->point, r->name);
        }
        free(r);

        return 0;
    }

【问题讨论】:

  • I'm in trouble...我们不会向您提供明确的问题陈述,我们也一样。
  • 阅读行时fgets 可能是更好的选择。
  • how_many_letter()fscanf(fp, " %*s%n", &amp;letter); 没有提供足够的参数来写入数据。同样在how_many_records().

标签: c linked-list malloc


【解决方案1】:

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

这里,y 未初始化。

您需要输入y = 0,或者,IMO,最好使用&amp;(r-&gt;point) [r[y].name 也是如此]

建议:阅读和解析整行最好使用fgets()

【讨论】:

    【解决方案2】:

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

    让我们从循环开始:

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

    无处在循环中你初始化r-&gt;next,所以在第一次迭代之后你会让y指向完全随机的内存,导致undefined behavior

    正如另一个答案中提到的,您没有初始化变量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 %s\n", 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] = '\0';  // "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 != '\0' && 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);
    }
    

    【讨论】:

    • 这也许不错,但我必须准确计算名称的字母和 malloc。在代码中必须是 2d malloc。
    • @ÁgostonDauner 好的,但是计算内存缓冲区中的字母仍然比尝试在文件中计算更容易(只需找到第一个空格,然后计算字符(但请记住 @ 987654323@ 读取并在缓冲区末尾留下换行符))。
    【解决方案3】:
    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 %s\n", 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 %s\n", first->point, first->name);
            first = first->next;
        }
    }
    

    【讨论】:

    • 嗨,这很好。最后一件事,我想写一个函数,它打印列表。我可以将整个主函数移动到一个 void 函数(没有 ranlist *r 和 *first,它们留在主函数中)。如果我将 printf 移动到另一个函数,则 printf 不起作用。缺少哪个变量? (对不起,我的英语不好)
    • @ÁgostonDauner 我认为最好将列表指针的开头(first)传递给函数。致电theFunc(first) main.
    • 缺少哪个变量?不看你的代码不知道有什么不好。
    猜你喜欢
    • 2015-06-09
    • 1970-01-01
    • 1970-01-01
    • 2015-09-25
    • 1970-01-01
    • 2018-06-21
    • 2019-04-27
    • 1970-01-01
    • 2012-08-16
    相关资源
    最近更新 更多