c - 将文件中的内容添加到链表

标签 c file input linked-list

我是 C 编程的新手。

我想将文件中的内容添加到链接列表。

文件中的内容是名称,每个名称都应该有自己的节点。

但是,当运行代码时,我得到一个无限循环。
我试图解决,但我无法深入了解它。
我有一种感觉是 fscanf 导致了这个问题。

先感谢您。

#include <stdio.h>
#include <stdlib.h> 
#include <string.h>

  typedef struct node {
    char name[50];
    struct node * next;
  }
node;

int main() {

  FILE * file = fopen("content.txt", "r");
  if (file == NULL)
    return 1;

  char buf[50];

  node * first = NULL;

  //read the contents of the file and write to linked list
  while (!feof(file)) {
    fscanf(file, "%s", buf);

    // try to instantiate node
    node * head = malloc(sizeof(node));
    if (head == NULL) {
      return -1;
    }

    head-> next = NULL;

    // add new word to linked list
    strcpy(head-> name, buf);

    //move node to start of linked list
    head-> next = first;
    first = head;

  }

  fclose(file);
  node * ptr = first;
  while (ptr != NULL) {
    printf("%s\n", ptr-> name);
  }

  return 0;

}

这就是输入文件的样子。
REDA
REDB
REDC
REDE
RED 
REDb
REDc
REDpikachu
REDboom

最佳答案

您的代码中有许多问题需要纠正(比其他问题更多)。

您的第一个问题可以通过阅读 Why is while ( !feof (file) ) always wrong? 来解决. (简短的答案是 EOF 不是在最终的 上生成 fscanf 读取,因此在读取最后一个“名称”后,您检查 !feof(file) 测试 TRUE ,循环继续, fscanf 失败并显示 EOF ,您分配然后尝试从 strcpy 获取不确定值的 buf —— 调用未定义行为)

在这种情况下,如果阅读没有空格的单词,您可以使用 fscanf ——但不要。早点养成好习惯。从文件的每一行获取输入时,使用面向行的输入函数(例如 fgets 或 POSIX getline )以确保输入缓冲区中剩余的内容不依赖于 scanf使用的转换说明符。

例如,您可以:

#define MAXN 50     /* if you need a constant, #define one (or more) */

typedef struct node {
    char name[MAXN];
    struct node *next;
}
node;

...
/* 读取文件内容并写入链表 */
while (fgets (buf, MAXN, file)) {
        /* valdiating the last char is '\n' or length < MAXN - 1 omitted */

        /* allocate new node */
        node *head = malloc (sizeof(node));
        if (head == NULL) {         /* validate allocation */
            perror ("malloc-node"); /* issue error message */
            return 1;   /* don't return negative values to the shell */
        }

        /* trim '\n' from end of buf */
        buf[strcspn(buf, "\n")] = 0;    /* overwrite with nul-character */

        /* initialize node values */
        strcpy (head->name, buf);
        head->next = NULL;

( 注意: 不要将负值返回到您的 shell,并且在引用结构的成员时,在 -> 运算符之后不要包含 空格 )

至此,您已成功将“姓名”读入buf ,分配给您的新节点,已验证 分配成功,修剪尾随'\n'包含在 buf 中通过面向行的输入功能,将修剪后的“名称”复制到head->name并初始化head->nextNULL .但是,你有一个问题。您的所有姓名都以相反的顺序存储在您的列表中(有时可能是您想要的)。但这通常不是所需的行为。通过简单地声明一个更新为指向最后一个节点的附加指针,您可以按顺序插入到列表中,而无需迭代查找最后一个节点。

例如,简单地声明一个 last指针和检查两种情况(一)last=NULL指示循环为空并制作 firstlast指向first node 允许您(2)在末尾添加所有其他节点,例如:
    node *first = NULL, *last = NULL;   /* use last for in-order chaining */
    ...
        /* initialize node values */
        strcpy (head->name, buf);
        head->next = NULL;

        /* in-order add at end of list */
        if (!last)
            first = last = head;
        else {
            last->next = head;
            last = head;
        }
    }

@CS Pei 正确解决了您的“无限循环”您只是忘记将当前指针前进到 ptr->next在你的输出循环中。

您还未能free您分配的内存。是的,它在程序退出时被释放,但养成跟踪您分配的所有内存的习惯,然后在不再需要时释放该内存。 (随着代码的增长,这成为防止内存泄漏的一项重要技能)。例如,在您的输出循环中,您可以执行以下操作:
    node *ptr = first;              /* declare/set pointer to first */
    while (ptr != NULL) {           /* loop while ptr */
        node *victim = ptr;         /* declare/set pointer to victim */
        printf ("%s\n", ptr->name); /* output name */
        ptr = ptr->next;            /* advance pointer to next */
        free (victim);              /* free victim */
    }

总而言之,您可以执行类似于以下的操作:
#include <stdio.h>
#include <stdlib.h> 
#include <string.h>

#define MAXN 50     /* if you need a constant, #define one (or more) */

typedef struct node {
    char name[MAXN];
    struct node *next;
}
node;

int main (int argc, char **argv) {

    FILE *file = argc > 1 ? fopen (argv[1], "r") : stdin;
    if (file == NULL)
        return 1;

    char buf[MAXN];

    node *first = NULL, *last = NULL;   /* use last for in-order chaining */

    /* read the contents of the file and write to linked list */
    while (fgets (buf, MAXN, file)) {

        /* valdiating the last char is '\n' or length < MAXN - 1 omitted */

        /* allocate new node */
        node *head = malloc (sizeof(node));
        if (head == NULL) {         /* validate allocation */
            perror ("malloc-node"); /* issue error message */
            return 1;   /* don't return negative values to the shell */
        }

        /* trim '\n' from end of buf */
        buf[strcspn(buf, "\n")] = 0;    /* overwrite with nul-character */

        /* initialize node values */
        strcpy (head->name, buf);
        head->next = NULL;

        /* in-order add at end of list */
        if (!last)
            first = last = head;
        else {
            last->next = head;
            last = head;
        }
    }

    if (file != stdin)  /* close file if not stdin */
        fclose(file);

    node *ptr = first;              /* declare/set pointer to first */
    while (ptr != NULL) {           /* loop while ptr */
        node *victim = ptr;         /* declare/set pointer to victim */
        printf ("%s\n", ptr->name); /* output name */
        ptr = ptr->next;            /* advance pointer to next */
        free (victim);              /* free victim */
    }

    return 0;
}

示例使用/输出

使用您的输入文件,您将获得以下输出:
$ ./bin/ll_chain_last <dat/llnames.txt
REDA
REDB
REDC
REDE
RED
REDb
REDc
REDpikachu
REDboom

内存使用/错误检查

在您编写的任何动态分配内存的代码中,对于分配的任何内存块,您有两个责任:(1)始终保留指向内存块起始地址的指针,(2)它可以在没有时被释放更需要。

您必须使用内存错误检查程序来确保您不会尝试访问内存或写入超出/超出分配 block 的范围,尝试读取或基于未初始化的值进行条件跳转,最后确认释放所有分配的内存。

对于 Linux valgrind是正常的选择。每个平台都有类似的内存检查器。它们都易于使用,只需通过它运行您的程序即可。
$ valgrind ./bin/ll_chain_last <dat/llnames.txt
==24202== Memcheck, a memory error detector
==24202== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==24202== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==24202== Command: ./bin/ll_chain_last
==24202==
REDA
REDB
REDC
REDE
RED
REDb
REDc
REDpikachu
REDboom
==24202==
==24202== HEAP SUMMARY:
==24202==     in use at exit: 0 bytes in 0 blocks
==24202==   total heap usage: 9 allocs, 9 frees, 576 bytes allocated
==24202==
==24202== All heap blocks were freed -- no leaks are possible
==24202==
==24202== For counts of detected and suppressed errors, rerun with: -v
==24202== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

始终确认您已释放所有已分配的内存并且没有内存错误。

如果您还有其他问题,请仔细查看并告诉我。

关于c - 将文件中的内容添加到链表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53824683/

相关文章:

c - 网上有没有 C 术语的汇编?

c - bpf 如何检查系统调用参数

c++ - C/C++ 新手使用 Android NDK 移植 Legacy 代码,出现编译错误

java - 删除finally block 中的目录?

c - 如何打印 5 位访问代码的 32 种不同组合(每个数字有 2 个选择(2^5))?

javascript - 当第一个和第二个文本输入值没有 ID 时,更改它们

objective-c - 如何测试定义的宏在编译时是否为空(Cbjective-C/c)?

java - 读取输入文件到链表的节点错误

html - Chrome 42 中的样式输入占位符(默认和焦点状态)

java - 从文件中读取但不保存到我的数组中