读取文本文件并将数据保存在链接列表中

时间:2018-10-29 10:48:17

标签: c linked-list

我想阅读一些不同类别的文本文件,并构建一个链接列表,该列表由这些文本文件中的不同单词以及该单词在文本文件中出现的总次数组成。链接列表应按字母顺序排列。但是,当我运行代码时,只有三个不同的单词被打印了数百次,它们的出现值始终为1。读取所有单词没有问题。我通过在while循环中添加printf语句对它进行了测试,它可以正确打印所有单词。我猜插入函数有一些问题。

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

typedef struct Term {
    char * termName;
    int occur;
    struct Term * next;
} term;

term * insert(term * root, char * word);

int main (void) {

    setlocale(LC_ALL, "Turkish");

    FILE *fPtr;

    int counter = 1;

    char path[50];

    snprintf(path, sizeof(path), "dataset\\econ\\%d.txt", counter);

    term * terms;
    terms = NULL;

    while (fPtr = fopen(path, "r")) {
        while(!feof(fPtr)) {
            char word[20];          
            fscanf(fPtr, "%s", &word);
            terms = insert(terms, word);
        }
        fclose(fPtr);   
        counter++;
        snprintf(path, sizeof(path), "dataset\\econ\\%d.txt", counter);
    }

    counter = 1;
    snprintf(path, sizeof(path), "dataset\\health\\%d.txt", counter);   

    while (fPtr = fopen(path, "r")) {
         while(!feof(fPtr)) {
            char word[20];          
            fscanf(fPtr, "%s", &word);
            terms = insert(terms, word);
        }
        fclose(fPtr);   
        counter++;
        snprintf(path, sizeof(path), "dataset\\health\\%d.txt", counter);
    }

    counter = 1;
    snprintf(path, sizeof(path), "dataset\\magazin\\%d.txt", counter);

    while (fPtr = fopen(path, "r")) {
        while(!feof(fPtr)) {
            char word[20];          
            fscanf(fPtr, "%s", &word);
            terms = insert(terms, word);
        }
        fclose(fPtr);
        counter++;
        snprintf(path, sizeof(path), "dataset\\magazin\\%d.txt", counter);
    }

    fclose(fPtr);

    while (terms -> next != NULL) {
        printf("%s: %d\n", terms -> termName, terms -> occur);
        terms = terms -> next;
    }
}

term * insert(term * root, char * word) {
    if (root == NULL) {
        root = (term *)malloc(sizeof(term));
        root -> next = NULL;
        root -> termName = word;
        root -> occur = 1;
        return root;
    } else if((strcmp(root-> termName, word)) < 0) {
        term * temp = (term *)malloc(sizeof(term));
        temp -> termName = word;
        temp -> occur = 1;
        temp -> next = root;
        return temp;
    } else {
        term * iter = root;
        while ((iter -> next != NULL) && (strcmp(iter -> termName, word) > 
0)) {           
            iter = iter -> next;
            if (strcmp(iter -> termName, word) == 0) {
                iter -> occur += 1;
                return root;
            }
        }       
        term * temp = (term *)malloc(sizeof(term));
        temp -> next = iter -> next;
        iter -> next = temp;
        temp -> termName = word;
        temp -> occur = 1;
        return root;        
    }
}

1 个答案:

答案 0 :(得分:2)

这行(和其他喜欢的行)就是问题。

temp -> termName = word;

您要分配word所指向的对象,即您在此处声明的数组:

char word[20];

哪个范围仅限于最内部的while循环。一旦循环结束,数组使用的内存就是公平的游戏,并且可以由另一个变量使用,这意味着未定义的行为会严重打击您的代码。最终您得到任何可识别的单词都是纯粹的运气。

因此,可以使用以下两种方法之一对其进行复制

temp -> termName = strdup(word);

temp -> termName = malloc(strlen(word)+1); // Always remember to allocate enough space for the NUL terminating character
strcpy(temp->termName, word);

也不要忘记释放它。

之所以您也没有看到计数增加,是因为从理论上讲应该这样做,因为传入的word与列表节点中的字符串相同,是因为您检查是否列表中已经存在的单词是错误的。

检查字符串是否相同是正确的,但是您永远不会触发它,因为while循环仅检查字符串是否在当前节点之后。

    while ((iter -> next != NULL) && (strcmp(iter -> termName, word) > 0)) {           

将这段代码移到while循环之外更有意义

if (strcmp(iter -> termName, word) == 0) {
    iter -> occur += 1;
    return root;
}

更详细地了解它,else例程的整个insert部分在当前编写时无法正常工作。

想象一下,如果要在其中插入以下字符串:“ A”,“ C”和“ E”。您的代码将以相反的顺序添加它们,因此您将在输出中得到“ E”,“ C”,“ A”。

如果您随后尝试添加“ D”,则会将其放在“ C”之后。首先将其与“ D”进行比较,然后strcmp将返回一个正数。然后将其与“ C”进行比较,循环将停止,因为它将返回负数。然后,在“ C”之后的“ ”之后添加“ D”。

根据上一个块if((strcmp(root-> termName, word)) < 0),当strcmp返回负值时,您要在要比较的节点之前插入新节点。但是您不能这样做,因为您不知道上一个节点是什么。

因此,通过组合这两位代码,加上对上一个节点的跟踪以及其他一些调整,您的insert函数将变为:

term * insert(term * root, char * word) {
    if (root == NULL) {
        root = (term *)malloc(sizeof(term));
        root -> next = NULL;
        root -> termName = strdup(word);
        root -> occur = 1;
        return root;
    } else {
        term * iter = root, *last = NULL;
        while ((iter != NULL) && (strcmp(iter -> termName, word) > 0)) {        
            last = iter;
            iter = iter -> next;
        }

        if ((iter)&&(strcmp(iter -> termName, word) == 0)) {
            iter -> occur += 1;
            return root;
        } else if (last == NULL) {
            term * temp = (term *)malloc(sizeof(term));
            temp -> termName = strdup(word);
            temp -> occur = 1;
            temp -> next = root;
            return temp;
        } else {
            term * temp = (term *)malloc(sizeof(term));
            temp -> next = last -> next;
            last -> next = temp;
            temp -> termName = strdup(word);
            temp -> occur = 1;
            return root;
        }
    }
}

因此,现在正在检查以找到不再位于当前节点之前的字母顺序的节点。如果节点具有相同的单词,则更新occur。如果尚未设置last,则意味着我们在开始位置,因此请在开始位置添加新节点并返回它。最后,我们知道last节点在此单词之后,而iter节点在此单词之前(或不存在),因此我们在两者之间插入了新单词。