добавление содержимого из файла в связанные списки - PullRequest
0 голосов
/ 18 декабря 2018

Я довольно новичок в программировании на Си.

Я хочу добавить содержимое из файла в список ссылок.

Содержимое файла - это имена, и каждое имя должно иметь свое собственноеузел.

Однако при запуске кода я получаю бесконечный цикл.Я пытался решить, но я просто не могу докопаться до сути.У меня такое ощущение, что 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

Ответы [ 3 ]

0 голосов
/ 18 декабря 2018
#include <stdio.h>
#include <stdlib.h> 
#include <string.h>

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

void list_insert(node **head,char *data)
{
    node *new;

    new = malloc(sizeof(node));
    new->next = *head;
    strcpy(new->name,data);
    *head = new;
}

int main() {
    char buf[50];
    node * first = NULL;

    FILE * file = fopen("content.txt", "r");
    if (file == NULL) {
        return 1;
    }
    while (!feof(file)) {
      fscanf(file, "%s", buf);
      list_insert(&first,buf);
    }
    fclose(file);

    node * ptr = first;
    while (ptr != NULL) {
        printf("%s\n", ptr-> name);
        ptr = ptr->next;
    }
    return 0;
}
0 голосов
/ 18 декабря 2018

У вас есть ряд проблем, которые необходимо исправить в вашем коде (некоторые из них больше, чем другие).

Ваша первая проблема может быть решена простым чтением Почему while (! Feof (file)) всегда неверно? .(краткий ответ: 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;

( note: не возвращает отрицательных значений в вашу оболочку и НЕ содержит пробел после оператора -> при обращении к членам структуры)

На этом этапе вы успешно прочитали «имя» в buf, выделенном для вашего нового узла, validated распределение выполнено успешно, обрезка завершающего '\n', включенного в buf, с помощью линейно-ориентированной функции ввода, копирование обрезанного «имени» в head->name и инициализация head->next в NULL.Однако у вас есть проблема.Все ваши имена хранятся в вашем списке в в обратном порядке (что иногда может быть тем, что вы хотите).Но это обычно не желаемое поведение.Просто объявив один дополнительный указатель, который обновляется так, чтобы он указывал на последний узел, вы можете выполнить вставку по порядку в список без итерации, чтобы найти последний узел.

Например, просто объявив last указатель и проверка двух случаев (1) last=NULL, указывающих, что цикл пуст, а указание first и last на узел first позволяет (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

Проверка использования памяти / ошибок

В любом кодеВы пишете, что динамически распределяет память, у вас есть 2 обязанностей относительно любого выделенного блока памяти: (1) всегда сохраняйте указатель на начальный адрес для блока памяти, поэтому, (2) он может быть освобожден , когда он больше не нужен.

Крайне важно, чтобы вы использовали программу проверки ошибок памяти, чтобы убедиться, что вы не пытаетесь получить доступ к памяти или писать за пределами / за ее пределами.границы вашего выделенного блока, попытайтесь прочитать или основать условный переход на неинициализированном значении и, наконец, подтвердить, что вы освобождаете всю выделенную память.

Для 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)

Всегда подтверждайте, что вы освободили всю выделенную память и что ошибок памяти нет.

Посмотрите вещи и дайте мне знать, если у вас есть дополнительные вопросы.

0 голосов
/ 18 декабря 2018

Я думаю, что проблема в выходной части.

  while (ptr != NULL) {
    printf("%s\n", ptr-> name); // here is the infinite loop
    ptr = ptr->next; // add this 
  }

Вы можете исправить это, добавив строку для обновления значения ptr, как показано выше.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...