Мой связанный список печатает последнее слово в моем текстовом файле для всех элементов - PullRequest
0 голосов
/ 21 октября 2018

Этот код читает текстовый файл и вставляет каждое слово в связанный список.Я новичок в связанных списках и работаю над этим в течение четырех часов и не могу до конца жизни понять это.

Так что же здесь происходит?Я проверил все, что знаю, и из-за своей жизни не могу правильно распечатать связанный список.Я считаю, что это как-то связано с функциями push / append.Каким-то образом это переписывает все ранее в связанном списке.Может быть, функция printlist перезаписывает все, но я не понимаю, как это возможно.

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

// A complete working C program to demonstrate all insertion methods
// on Linked List  
// A linked list node
struct Node;
void push(struct Node **head_ref, char *new_data);
void insertAfter(struct Node *prev_node, char *new_data);
void append(struct Node **head_ref, char *new_data);
void printList(struct Node *node);
int LINECOUNT(FILE *(*stream), char *filename);

struct Node {
    char *data;
    struct Node *next;
};

/* Given a reference (pointer to pointer) to the head of a list and 
   an int, inserts a new node on the front of the list. */
void push(struct Node **head_ref, char *new_data) {
    /* 1. allocate node */
    struct Node* new_node = (struct Node *)malloc(sizeof(struct Node));

    /* 2. put in the data  */
    new_node->data  = new_data;
    printf("push data:%s ", new_data);

    /* 3. Make next of new node as head */
    new_node->next = (*head_ref);

    /* 4. move the head to point to the new node */
    (*head_ref) = new_node;
}

/* Given a reference (pointer to pointer) to the head
   of a list and an int, appends a new node at the end  */
void append(struct Node **head_ref, char *new_data) {
    /* 1. allocate node */
    struct Node* new_node = (struct Node *)malloc(sizeof(struct Node));
    struct Node *last = *head_ref;  /* used in step 5*/

    /* 2. put in the data  */
    new_node->data  = new_data;
    printf("push data:%s ", new_data);

    /* 3. This new node is going to be the last node, so make next of
          it as NULL*/
    new_node->next = NULL;

    /* 4. If the Linked List is empty, then make the new node as head */
    if (*head_ref == NULL) {
       *head_ref = new_node;
       return;
    }

    /* 5. Else traverse till the last node */
    while (last->next != NULL)
        last = last->next;

    /* 6. Change the next of last node */
    last->next = new_node;
    return;
}

// This function prints contents of linked list starting from head
void printList(struct Node *node) {
    while (node != NULL) {
        printf(" %s ", node->data);
        node = node->next;
    }
}

int LINECOUNT(FILE *(*stream), char *filename) {            
    int size = 0;
    size_t chrCount;
    char *text;

    if ((*stream = fopen(filename, "r")) == NULL) {
        printf("LC Could not open hw8 data file.\n");
        exit(0);
    }
    while (1) {
        text = NULL;
        getline(&text, &chrCount, *stream); 
        free(text); /*free text*/
        if (feof(*stream))
            break;
        size++;
    }
    rewind(*stream);
    return size;
}

/*int wordCount(FILE *(*stream), char *filename, int lineCount) {
    char ch;
    int wordcount = 0;
    int charcount = 0; 
    *stream = fopen(filename, "r");
    int x = 0;
    int lineArr[lineCount];
    for (int i = 0; i < lineCount; i++) {
        lineArr[i] = 0;
    }
    if (*stream) {
        while ((ch = getc(*stream)) != EOF) {
            if (ch != ' ' && ch != '\n') {
                charcount++;
            }
            if (ch == ' ' || ch == '\n') { 
                wordcount++;
                lineArr[x] = lineArr[x] + 1;
            }
            if (ch == '\n') {
                x++;
            }
        }
        if (charcount > 0) {
            wordcount++;
            charcount++;
        }
    } else {
        printf("Failed to open the file\n");
    }

    // rewind(*stream);
    return lineArr;
}*/

int main(void) {
    char *fn = "hw8data.txt";
    int lineCount;
    FILE *stream;

    lineCount = LINECOUNT(&stream, fn);
    //int lineArr[lineCount];
    //int lineArr[];//lineArr[0] = 4 would say the first line has 4 words. using this data for strtok

    //lineArr = wordCount(&stream, fn, lineCount);

    //-------------------------------------
    char ch;
    int wordcount = 0;
    int charcount = 0; 
    stream = fopen("./hw8data.txt", "r");
    int x = 0;
    int lineArr[lineCount];
    for (int i = 0; i < lineCount; i++) {
        lineArr[i] = 0;
    }
    if (stream) {
        while ((ch = getc(stream)) != EOF) {
            if (ch != ' ' && ch != '\n') {
                charcount++;
            }
            if (ch == ' ' || ch == '\n') {
                wordcount++;
                lineArr[x] = lineArr[x] + 1;
            }
            if (ch == '\n') {
                x++;
            }
        }
        //if (charcount > 0) { wordcount++; charcount++; }
    } else {
        printf("Failed to open the file\n");
    }
    /* Start with the empty list */
    struct Node *head = NULL;
    rewind(stream);
    char *sArr = malloc(42 * sizeof(char));

    fscanf(stream, "%s ", sArr);
    printf("%s ", sArr);
    push(&head, sArr);
    fscanf(stream, "%s ", sArr);
    printf("%s ",sArr);
    append(&head, sArr);
    printList(head);
    return 0;
}

Ответы [ 2 ]

0 голосов
/ 21 октября 2018

Я бы предложил совершенно другой подход.

Я бы использовал элемент гибкого массива C99 для хранения каждого слова.Кроме того, поскольку я не хочу, чтобы мой код передавался в качестве ответа на домашнее задание, я покажу, как это сделать с помощью широких символов .(Практически во всех ОС, кроме, возможно, Windows, он обрабатывает не-ASCII-символы, такие как Ö и Ø, как буквы, если ваша локаль говорит, что они есть.)

struct word {
    struct word  *next;
    wchar_t       data[];  /* Flexible array member */
};

Я бы использовал вспомогательную функцию, которая читает следующее словоиз широкого потока, пропуская любые несловарные символы (которые я предполагаю, что они являются буквенно-цифровыми символами, то есть буквами и цифрами):

struct word *wide_word(FILE *input)
{
    struct word *w = NULL, *tempw;
    size_t       max = 0;  /* No characters allocated in w yet */
    size_t       len = 0;  /* No characters in w yet */
    wint_t       c;

    /* NULL input is not allowed. */
    if (!input) {
        errno = EINVAL;
        return NULL;
    }

    /* Also fail if the stream is already in an error state. */
    if (ferror(input)) {
        errno = EIO;
        return NULL;
    }

    c = getwc(input);

    /* Skip leading non-word characters. */
    while (c != WEOF && !iswalnum(c))
        c = getwc(input);

    /* End of input? */
    if (c == WEOF) {
        errno = 0;
        return NULL;
    }

    /* Append each wide word character. */
    while (c != WEOF && iswalnum(c)) {

        /* Need to reallocate? */
        if (len >= max) {
            /* Since words are usually short, let's allocate
               in chunks of 64 wide characters. */
            max = (len | 63) + 65;

            tempw = realloc(w, sizeof (struct word) + max * sizeof (wchar_t));
            if (!tempw) {
                /* Out of memory. */
                free(w);
                errno = ENOMEM;
                return NULL;
            }

            w = tempw;
        }

        /* Append. */
        w->data[len++] = c;

        c = getwc(input);
    }

    /* Although not useful for this particular case,
       we normally want to keep the separator intact. */
    if (c != WEOF)
        ungetwc(c, input);

    /* Optimize the memory allocated to the word. */
    if (max != len + 1) {
        max = len + 1;
        tempw = realloc(w, sizeof (struct word) + max * sizeof (wchar_t));
        if (!tempw) {
            free(w);
            errno = ENOMEM;
            return NULL;
        }
        w = tempw;
    }

    /* Terminate the wide string in w. */
    w->data[len] = L'\0';

    /* Success! */
    return w;
}

Я лично предпочитаю добавлять в список новые узлы, а затем полностью переворачиватьсписок после:

struct word *reverse_list(struct word *oldlist)
{
    struct word *newlist = NULL;
    struct word *w;

    while (oldlist) {
        w = oldlist;
        oldlist = oldlist->next;

        w->next = newlist;
        newlist = w;
    }

    return newlist;
}

С учетом вышесказанного, программа для чтения широких слов из стандартного ввода в основном

#define  _POSIX_C_SOURCE  200809L
#include <stdlib.h>
#include <locale.h>
#include <stdio.h>
#include <wchar.h>
#include <errno.h>

/* Functions listed above */

int main(void)
{
    struct word  *list, *node;

    if (!setlocale(LC_ALL, ""))
        fprintf(stderr, "Warning: Your C library does not support your current locale.\n");

    if (fwide(stdin, 1) < 1)
        fprintf(stderr, "Warning: Your C library does not support wide standard input.\n");

    if (fwide(stdout, 1) < 1)
        fprintf(stderr, "Warning: Your C library does not support wide standard output.\n");

    /* Read words from standard input into reversed list. */
    while (1) {
        node = wide_word(stdin);
        if (!node) {
            if (errno) {
                fprintf(stderr, "Error reading standard input: %s.\n", strerror(errno));
                exit(EXIT_FAILURE);
            }
            /* No error, just end of input. */
            break;
        }

        /* Prepend to list. */
        node->next = list;
        list = node;
    }

    /* Reverse the list so first word is first in list. */
    list = reverse_list(list);

    /* Print each word in the list to standard output, in order. */
    for (node = list; node != NULL; node = node->next)
        wprintf(L"%ls\n", node->data);

    /* We could free() each word in 'list' here. */

    return EXIT_SUCCESS;
}
0 голосов
/ 21 октября 2018
  char* sArr=malloc(42*sizeof(char));

  fscanf(stream,"%s ",sArr);
  printf("%s ",sArr);
  push(&head,sArr);
  fscanf(stream,"%s ",sArr);
  printf("%s ",sArr);
  append(&head,sArr);

Вы добавляете одно и то же значение в список дважды, значение, которое вы получили от своего единственного вызова, только malloc.Если вы хотите, чтобы два узла содержали разные значения, не добавляйте одно и то же значение дважды.Одно неприятное исправление - если после push(&head,sArr) добавить еще sArr = malloc(42*sizeof(char));.Таким образом, ваш вызов append добавит в список другое значение.

Если вы этого не видите, добавьте код для вывода значения node->data при печати списка.Вы увидите, что оба узла имеют указатели на один и тот же кусок памяти, значение, которое вы вернули после этого вызова malloc.

Но было бы намного элегантнее, если бы ваши записи в списке имели свое содержимое.Для этого потребуются такие функции, как push и append, чтобы выделять свои собственные указатели, копировать в них строки и использовать эти новые указатели.Ваш код для уничтожения списка может вызвать free как для данных, так и для узлов.

...