新手求助:使用malloc动态分配结构体读取文件失败问题
问题描述
尝试将details.txt文件中的值存入动态分配的Person结构体对应字段,但程序运行异常,无法正确解析数据,请问哪里操作有误?是否必须使用strtok按逗号分割数据?
details.txt 内容
mitch,8,1.78,burgers murray,42,6.5,lasagna travis,64,1.85,sushi sam,12,1.94,bacon
出错代码
#include <stdio.h> #include <string.h> #include <stdlib.h> #define BUFFERSIZE 256 typedef struct { char name[BUFFERSIZE]; int favnumber; float height; char favfood[BUFFERSIZE]; } Person; void print_person(Person *p) { printf("name: %s\n", p->name); printf("num: %d\nheight: %.2f\nfav. food: %s\n\n", p->favnumber, p->height, p->favfood); } int count_lines(FILE *fp) { int nlines = 0; char c; for (c = fgetc(fp); c != EOF; c = fgetc(fp)) { if (c == '\n') { nlines++; } } rewind(fp); return nlines; } int main(void) { FILE *fp = fopen("details.txt", "r"); // count lines int nlines = count_lines(fp); printf("found %d lines\n\n",nlines); Person *people = (Person*)malloc(nlines*sizeof(Person)); char buffer[BUFFERSIZE]; int i = 0; while (fgets(buffer,BUFFERSIZE,fp) != NULL) { sscanf(buffer,"%s%d%f%s",people[i].name, &(people[i].favnumber), &(people[i].height),people[i].favfood); print_person(&(people[i])); i++; } printf("found %d people\n",i); free(people); fclose(fp); }
当前程序输出
found 4 lines name: mitch,8,1.78,burgers num: 0 height: 0.00 fav. food: ... found 4 people
问题分析与解决
错误核心原因
sscanf的%s格式符默认以**空白字符(空格、换行、制表符)**作为读取结束标志,但你的数据是用逗号分隔的,没有空白字符。因此第一个%s会把整行内容全部读入name字段,后续的%d、%f、%s找不到匹配的输入,导致favnumber、height、favfood保留初始垃圾值(或默认0/空)。
解决方法:不一定非要用strtok
有两种简单可行的修正方式:
方式1:修改sscanf格式字符串(推荐简单场景)
利用sscanf的扫描集功能,指定读取到逗号为止:
sscanf(buffer, "%[^,],%d,%f,%[^\n]", people[i].name, &people[i].favnumber, &people[i].height, people[i].favfood);
%[^,]:读取所有非逗号的字符,精准匹配名字字段%[^\n]:读取到换行符为止,避免把行尾的换行符读入食物字段
方式2:使用strtok分割字符串(适合复杂格式)
如果后续数据格式可能变化,strtok的灵活性更高:
char *token = strtok(buffer, ",\n"); // 逗号和换行符都作为分隔符 strcpy(people[i].name, token); token = strtok(NULL, ",\n"); people[i].favnumber = atoi(token); token = strtok(NULL, ",\n"); people[i].height = atof(token); token = strtok(NULL, ",\n"); strcpy(people[i].favfood, token);
注意:strtok会修改原字符串,若需保留原始buffer内容,需先做拷贝。
额外优化点
- 修正行统计逻辑:原
count_lines会漏掉最后一行无换行符的情况,修改为:
int count_lines(FILE *fp) { int nlines = 0; char c; int prev = '\n'; while ((c = fgetc(fp)) != EOF) { if (c == '\n') nlines++; prev = c; } if (prev != '\n') nlines++; // 处理最后一行无换行的情况 rewind(fp); return nlines; }
- 增加内存/文件检查:避免空指针或文件打开失败的异常:
// 检查文件是否打开成功 FILE *fp = fopen("details.txt", "r"); if (fp == NULL) { perror("failed to open file"); return 1; } // 检查malloc是否成功 Person *people = malloc(nlines * sizeof(Person)); if (people == NULL) { perror("malloc failed"); fclose(fp); return 1; }
内容的提问来源于stack exchange,提问作者Akiyuu
相关产品推荐
相关产品推荐

