You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在C语言中读取以标点(含逗号)分隔的文本文件数据?

解决C语言中读取逗号分隔文本文件并填充BST的问题

你的代码目前有两个核心问题需要解决:一是无法正确读取带空格的姓名(比如John Brown),二是完全没处理第三个字段的好友ID数组。下面我会一步步帮你完善读取逻辑,同时兼顾内存安全和代码健壮性。


1. 先修正姓名读取的问题

原来用%s读取姓名会在空格处截断,因为%s只读取到空白字符为止。我们可以用%[^,]格式说明符——它会读取直到遇到逗号为止的所有字符,刚好匹配姓名字段(位于两个逗号之间)的格式。

需要注意:

  • 读取完第一个ID字段后,要用%*c跳过后面的逗号(确保不会把逗号当成姓名的一部分)
  • 读取姓名后,同样要跳过后面的逗号,才能读取第三个好友ID字段

2. 处理好友ID数组的解析

第三个字段是用-分隔的多个long long值,我们需要分三步处理:

  1. 先读取整个字段的字符串(用%[^\n]读取到换行符为止)
  2. 用strtok按-分割字符串
  3. 将分割后的每个字符串转换成long long,并动态分配内存存储这些ID

另外,建议给你的BST结构体加一个friendCount字段——不然没法知道好友ID数组的长度,后续遍历、释放内存都会出问题:

typedef struct BST {
    long long ID;
    char *name;
    char *surname;
    long long *friendsID;
    int friendCount; // 新增:记录好友ID的数量
    struct BST *left;
    struct BST *right;
} BST;

3. 完整的读取代码示例

下面是完善后的读取逻辑,包含内存分配、错误处理和字段解析:

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

// 修改后的BST结构体
typedef struct BST {
    long long ID;
    char *name;
    char *surname;
    long long *friendsID;
    int friendCount;
    struct BST *left;
    struct BST *right;
} BST;

// 辅助函数:分割字符串并转换为long long数组
long long* splitAndConvert(const char *str, int *count) {
    char *tempStr = strdup(str); // 复制原字符串,避免修改原始数据
    if (!tempStr) return NULL;

    long long *arr = NULL;
    *count = 0;

    char *token = strtok(tempStr, "-");
    while (token != NULL) {
        // 跳过可能的空白字符(如果数据格式有空格的话)
        while (isspace((unsigned char)*token)) token++;
        if (*token == '\0') {
            token = strtok(NULL, "-");
            continue;
        }

        // 转换为long long,同时检查转换是否成功
        char *endPtr;
        long long id = strtoll(token, &endPtr, 10);
        if (*endPtr != '\0') {
            free(arr);
            free(tempStr);
            return NULL;
        }

        // 动态扩容数组
        long long *newArr = realloc(arr, (*count + 1) * sizeof(long long));
        if (!newArr) {
            free(arr);
            free(tempStr);
            return NULL;
        }
        arr = newArr;
        arr[*count] = id;
        (*count)++;

        token = strtok(NULL, "-");
    }

    free(tempStr);
    return arr;
}

// 适配修改后的BST的插入函数(示例实现)
BST* insertNewUser(BST *root, long long ID, const char *fullName, long long *friendsID, int friendCount) {
    BST *newNode = malloc(sizeof(BST));
    if (!newNode) return root;

    newNode->ID = ID;
    newNode->friendCount = friendCount;
    newNode->friendsID = friendsID;
    newNode->left = newNode->right = NULL;

    // 分割姓名:找到最后一个空格,前面是名,后面是姓
    const char *lastSpace = strrchr(fullName, ' ');
    if (lastSpace) {
        newNode->name = malloc(lastSpace - fullName + 1);
        if (newNode->name) {
            strncpy(newNode->name, fullName, lastSpace - fullName);
            newNode->name[lastSpace - fullName] = '\0';
        }
        newNode->surname = strdup(lastSpace + 1);
    } else {
        // 如果没有空格,把整个字符串作为名,姓设为NULL
        newNode->name = strdup(fullName);
        newNode->surname = NULL;
    }

    // 这里补充BST的插入逻辑(根据ID大小插入左/右子树)
    if (root == NULL) return newNode;
    BST *current = root;
    while (1) {
        if (ID < current->ID) {
            if (current->left == NULL) {
                current->left = newNode;
                break;
            }
            current = current->left;
        } else {
            if (current->right == NULL) {
                current->right = newNode;
                break;
            }
            current = current->right;
        }
    }

    return root;
}

int main() {
    FILE *fp = fopen("data.txt", "r");
    if (!fp) {
        perror("Failed to open file");
        return 1;
    }

    BST *root = NULL;
    long long ID;
    char fullName[256]; // 假设姓名最长255字符,可按需调整
    char friendsStr[1024]; // 假设好友ID字符串最长1023字符

    // 读取每行的三个字段:ID、姓名、好友ID字符串
    while (fscanf(fp, "%lld%*c%[^,]%*c%[^\n]", &ID, fullName, friendsStr) == 3) {
        int friendCount = 0;
        long long *friendsID = splitAndConvert(friendsStr, &friendCount);
        
        // 处理转换失败的情况
        if (friendsID == NULL && friendCount != 0) {
            fprintf(stderr, "Invalid friends ID format for user %lld\n", ID);
            continue;
        }

        // 插入到BST
        root = insertNewUser(root, ID, fullName, friendsID, friendCount);
    }

    // 这里可以添加BST遍历、内存释放的代码
    // ...

    fclose(fp);
    return 0;
}

关键注意事项

  • 内存管理:所有动态分配的内存(name、surname、friendsID)在删除BST节点时必须手动释放,避免内存泄漏。
  • 字段长度限制:示例中用了固定大小的缓冲区,如果你的数据有更长的字段,建议改用fgets读取整行后再分割,避免缓冲区溢出。
  • 线程安全:如果是多线程环境,建议用strtok_r替代strtok,避免线程安全问题。
  • 错误处理:示例中加入了基本的错误处理,实际项目中可以根据需求扩展(比如处理空的好友ID字段)。

内容的提问来源于stack exchange,提问作者J.Doe123

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 06:48:27