通过使用 sscanf 提取数据在 BST 插入函数中传递参数

Passing arguments in a BST insert function by extracting data using sscanf

我正在编写一个程序,它从文本文件(逐行)中读取学生(id、姓名、姓氏、年级),然后使用 id 作为键将它们存储到二叉搜索树中。要阅读每一行,我使用 fgets() 并从该行中提取单词使用 sscanf().

struct TreeNode* root = NULL;
FILE *text;
char *id, *onoma, *epitheto, *word, *line;
onoma = (char *)malloc(20 * sizeof(char));
epitheto = (char *)malloc(30 * sizeof(char));
id = (char *)malloc(9 * sizeof(char));
float vathmos;

text = fopen("students.txt", "r");
if (text == NULL) {
    printf("Cannot read from the file!");
    exit(1);
}

这是为每个学生提取数据的循环:

while (fgets(line, 50, text) != NULL) {
    printf("%d \n", root);
    sscanf(line, "%s %s %s %f", id, onoma, epitheto, &vathmos);
    printf("%s  %s  %s  %.3f \n", id, onoma, epitheto, vathmos);
    root = Insert(root, id);
}

这是节点的插入函数:

TreeNode *Insert(struct TreeNode* root, char *data) {
    if (root == NULL) { // empty tree
        root = CreateNewNode(data);
    }
    // if data to be inserted is lesser, insert in left subtree.
    else if ((strcmp(data, root->id)) <= 0) {
        root->left = Insert(root->left,data);
    }
    // else, insert in right subtree.
    else if ((strcmp(data, root->id)) > 0) {
        root->right = Insert(root->right,data);
    }
    return root;
}

当我插入节点时 "by hand" 例如:

root = Insert(root, "AY881159");
root = Insert(root, "AA564510");
root = Insert(root, "AB784123");

程序运行,节点已创建,树可以被操作。

但是当通过从 sscanf() 获取数据在 fgets() 循环中创建树时,就会出现问题。虽然变量正确存储了数据(这就是为什么我在 sscanf() 之后有 printf() 来检查这一点),但 root 似乎已重置并且只有最后一个学生保留在树中。

有什么想法吗?

节点的代码是:

typedef struct TreeNode {
    char *id;
    struct TreeNode *left;
    struct TreeNode *right;
} TreeNode;

TreeNode *CreateNewNode(char *data) {
    struct TreeNode *NewNode = (TreeNode *)malloc(sizeof(TreeNode));
    NewNode->id = data;
    NewNode->left = NewNode->right = NULL;
    return NewNode;
}

您 post 编写的代码无法编译,这使得回答问题变得更加困难。

您从同一个 id 缓冲区创建循环中的所有节点。您需要制作缓冲区的副本,无论是在调用 Insert 还是最好在 CreateNewnode() 函数中。您没有 post 代码,也没有 post 类型 TreeNode 的定义。这是一种可能性:

TreeNode *CreatNewNode(const char *data) {
    TreeNode *node = calloc(1, sizeof(*node));
    if (node != NULL) {
        node->id = strdup(data);
        node->left = node->right = NULL;
    }
    return node;
}

不需要为解析阶段分配数组,本地 char 数组对此很好,但是存储到树中的数据应该被复制,这样您就可以重用解析代码中的缓冲区.使 Insert 的参数成为 const char *data 以指示缓冲区不会被修改,也不会在调用后由树拥有。

您必须将额外信息传递给 scanf 以防止缓冲区溢出。

这里是调用代码的修改版本:

int main(void) {
    struct TreeNode *root = NULL;
    FILE *text;
    char id[9], onoma[20], epitheto[30], line[256];
    float vathmos;

    text = fopen("students.txt", "r");
    if (text == NULL) {
        printf("Cannot read from the file!");
        exit(1);
    }

    // This is the loop where the data are extracted for each student:
    while (fgets(line, sizeof line, text) != NULL) {
        printf("%d \n", root);
        if (sscanf(line, "%8s %19s %29s %f", id, onoma, epitheto, &vathmos) == 4) {
            printf("%s  %s  %s  %.3f \n", id, onoma, epitheto, vathmos);
            root = Insert(root, id);
        } else {
            printf("invalid line: %s", line);
        }
        // I'm curious how you are going to store the other data...
    }
    ...
} 

Insert函数可以简化为:

TreeNode *Insert(struct TreeNode *root, const char *data) {
    if (root == NULL) { // empty tree
        root = CreateNewNode(data);
    } else {
        if (strcmp(data, root->id) <= 0) {
            // if data to be inserted is lesser or equal, insert in left subtree.
            root->left = Insert(root->left, data);
        } else {
            // else insert in the right subtree
            root->right = Insert(root->right, data);
        }
    }
    return root;
}

对于 InsertNode 更好的 API 将采用指向 root 指针的指针和 return 指向新节点的指针:

TreeNode *Insert(struct TreeNode **nodep, const char *data) {
     while (*nodep != NULL) {
        if (strcmp(data, (*nodep)->id) <= 0) {
            nodep = &(*nodep)->left;
        } else {
            nodep = &(*nodep)->right;
        }
    }
    return *nodep = CreateNewNode(data);
}