如何在C语言中读取以标点(含逗号)分隔的文本文件数据?
解决C语言中读取逗号分隔文本文件并填充BST的问题
你的代码目前有两个核心问题需要解决:一是无法正确读取带空格的姓名(比如John Brown),二是完全没处理第三个字段的好友ID数组。下面我会一步步帮你完善读取逻辑,同时兼顾内存安全和代码健壮性。
1. 先修正姓名读取的问题
原来用%s读取姓名会在空格处截断,因为%s只读取到空白字符为止。我们可以用%[^,]格式说明符——它会读取直到遇到逗号为止的所有字符,刚好匹配姓名字段(位于两个逗号之间)的格式。
需要注意:
- 读取完第一个ID字段后,要用
%*c跳过后面的逗号(确保不会把逗号当成姓名的一部分) - 读取姓名后,同样要跳过后面的逗号,才能读取第三个好友ID字段
2. 处理好友ID数组的解析
第三个字段是用-分隔的多个long long值,我们需要分三步处理:
- 先读取整个字段的字符串(用
%[^\n]读取到换行符为止) - 用
strtok按-分割字符串 - 将分割后的每个字符串转换成
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
相关产品推荐
相关产品推荐

