从文件读取数据到链表时sscanf匹配失败问题求助
问题描述
我编写了一段C语言代码,用于从President.txt文件读取总统数据到链表,但始终触发sscanf的错误判定分支。我已多次检查文件内容,确认格式无误,但仍无法正常读取数据。希望能获得问题排查帮助,或相关技术学习知识点我可以自行研究解决。
代码实现
#include <stdio.h> #include <stdlib.h> #include <string.h> #define MAX 100 typedef struct President { int rno; char name[MAX]; int start_date; int end_date; struct President *next; } President; void readDataFromFile(President **h) { FILE *fp = fopen("President.txt", "r"); if (fp == NULL) { printf("Cannot open a file!\n"); return; } char buffer[MAX]; int rno, start_date, end_date; char name[MAX]; while(fgets(buffer, MAX, fp) != NULL) { // Reading data from line if (sscanf(buffer, "%d %[^\n] %d-%d", &rno, name, &start_date, &end_date) != 4) { printf("Error trying to read file!\n"); fclose(fp); return; } // Creating of new node President *newNode = (President*)malloc(sizeof(President)); if (newNode == NULL) { printf("Memory error!\n"); fclose(fp); return; } newNode->rno = rno; strcpy(newNode->name, name); newNode->start_date = start_date; newNode->end_date = end_date; newNode->next = NULL; // Adding new node to list if (*h == NULL) { *h = newNode; } else { President* current = *h; while (current->next != NULL) { current = current->next; } current->next = newNode; } } fclose(fp); printf("Data is added to list!\n"); }
文件示例内容
1 President 2000-2004 2 President 1932-1957 3 President 2012-2016
问题排查与解决
核心问题
你的sscanf格式串存在两个致命问题:
%[^\n]匹配逻辑错误:该格式符会读取所有字符直到换行符,包括名字后面的空格和日期部分,导致后续的%d-%d完全无法匹配,返回值远小于4,触发错误分支。- 格式串转义错误:原代码中格式串里的
是未正确转义的换行符,实际应该写成\n,不过即使修正了这一点,第一个问题依然存在。
修复方案
方案1:修正sscanf格式串
如果名字是单个单词(无空格),直接用%s匹配名字即可,它会自动在空格处停止:
if (sscanf(buffer, "%d %s %d-%d", &rno, name, &start_date, &end_date) != 4) { printf("Error trying to read file!\n"); fclose(fp); return; }
如果名字包含空格(比如John Adams),需要先清理行尾的换行符,再用更精准的格式串,确保名字读取到日期前的空格为止:
while(fgets(buffer, MAX, fp) != NULL) { // 移除行尾的换行符和空白字符 buffer[strcspn(buffer, "\n")] = '\0'; // 用%[^0-9]匹配名字(直到遇到数字为止),并跳过后续空白 if (sscanf(buffer, "%d %[^0-9]%d-%d", &rno, name, &start_date, &end_date) != 4) { printf("Error trying to read file!\n"); fclose(fp); return; } // 清理名字末尾的空格 name[strcspn(name, " ")] = '\0'; // 后续节点创建逻辑不变... }
方案2:用strtok分步分割字符串(更可靠)
对于复杂格式的文本,分步分割比sscanf更可控,避免格式串匹配的歧义:
while(fgets(buffer, MAX, fp) != NULL) { buffer[strcspn(buffer, "\n")] = '\0'; // 分割编号 char *token = strtok(buffer, " "); if (!token) goto read_error; rno = atoi(token); // 分割名字(如果名字含空格,需要调整为合并到日期前的所有token) token = strtok(NULL, " "); if (!token) goto read_error; strcpy(name, token); // 分割日期部分 token = strtok(NULL, "-"); if (!token) goto read_error; start_date = atoi(token); token = strtok(NULL, " "); if (!token) goto read_error; end_date = atoi(token); // 节点创建与插入逻辑不变... continue; read_error: printf("Error trying to read file!\n"); fclose(fp); return; }
额外排查建议
- 检查
MAX宏的定义是否足够大,确保能容纳文件中最长的一行内容。 - 确认文件路径正确:如果President.txt不在程序运行的当前工作目录,需要使用绝对路径(比如
fopen("/home/user/President.txt", "r"))。 - 在错误分支中打印当前读取的buffer内容:
printf("Failed line: '%s'\n", buffer);,直观看到实际读取的内容,更容易定位匹配失败的原因。
内容的提问来源于stack exchange,提问作者Poizone
相关产品推荐
相关产品推荐

