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

新手求助:使用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内容,需先做拷贝。

额外优化点

  1. 修正行统计逻辑:原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;
}
  1. 增加内存/文件检查:避免空指针或文件打开失败的异常:
// 检查文件是否打开成功
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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 04:35:26