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

C语言如何在未知行数时读取CSV文件至末尾?

解决CSV文件动态读取(无需预先知晓行数)的问题

嘿,我来帮你搞定这个CSV读取的难题!你之前遇到的前8行全0的异常,大概率是文件指针位置不对或者初始化/解析逻辑出了问题,咱们一步步来解决:

先排查你之前fgets方法的异常原因

你提到用char buffer[1000]; while(fgets(buffer, 1000, fp)) {}出现前8行全0,常见的坑有这几个:

  • 文件指针没在开头:如果打开文件后你先做了其他操作(比如用fseek跳到某个位置,或者之前读取过一部分内容没重置),fgets会从当前指针位置开始读,前面的行自然读不到,数组初始值是0就会显示全0。解决方法:打开文件后调用rewind(fp)确保指针回到文件开头。
  • 没跳过CSV表头:如果你的CSV第一行是表头(比如字段名),直接把表头解析成数据的话,可能会出现解析失败(比如把字符串转成数字得到0),看起来像前几行全0。
  • 数组未初始化:如果你用来存储数据的数组是全局变量或者未手动初始化,默认值是0,要是读取逻辑没覆盖到前8行,就会显示全0。

正确的动态读取方案(无需预先知道行数)

不用在CSV里存行数,咱们用动态数组来存储数据,随着读取过程自动扩容,完美适配任意行数的CSV。下面是完整的实现示例(结合你的readBeer场景,假设你要读取啤酒数据):

1. 定义数据结构体(根据你的CSV字段调整)

typedef struct {
    char beerName[64];
    float alcoholContent;
    int productionYear;
    // 其他字段根据你的CSV实际内容添加
} Beer;

2. 动态读取并解析CSV的完整代码

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

typedef struct {
    char beerName[64];
    float alcoholContent;
    int productionYear;
} Beer;

// 解析单一行CSV到Beer结构体
int parseBeerLine(const char *line, Beer *beer) {
    if (!line || !beer) return 0;

    // 复制一行内容到临时缓冲区,避免修改原字符串
    char tempBuffer[1000];
    strncpy(tempBuffer, line, sizeof(tempBuffer)-1);
    tempBuffer[sizeof(tempBuffer)-1] = '\0';

    // 分割CSV字段(假设格式是:啤酒名称,酒精度,生产年份)
    char *token = strtok(tempBuffer, ",");
    if (!token) return 0;
    strncpy(beer->beerName, token, sizeof(beer->beerName)-1);
    beer->beerName[sizeof(beer->beerName)-1] = '\0';

    token = strtok(NULL, ",");
    if (!token) return 0;
    beer->alcoholContent = atof(token);

    token = strtok(NULL, ",");
    if (!token) return 0;
    // 去掉换行符(fgets会把换行符读进来)
    token[strcspn(token, "\n\r")] = '\0';
    beer->productionYear = atoi(token);

    return 1;
}

int main() {
    FILE *fp = fopen("beers.csv", "r");
    if (!fp) {
        perror("无法打开CSV文件");
        return 1;
    }

    // 初始化动态数组:初始容量设为4,后续自动扩容
    Beer *beerList = NULL;
    size_t currentCapacity = 4;
    size_t totalRows = 0;

    // 跳过CSV表头(如果你的CSV没有表头,就删掉这段)
    char lineBuffer[1000];
    if (fgets(lineBuffer, sizeof(lineBuffer), fp) == NULL) {
        fclose(fp);
        return 1;
    }

    // 循环读取到文件末尾
    while (fgets(lineBuffer, sizeof(lineBuffer), fp)) {
        // 检查是否需要扩容:当前行数达到容量时,把容量翻倍
        if (totalRows >= currentCapacity) {
            currentCapacity *= 2;
            Beer *tempList = realloc(beerList, currentCapacity * sizeof(Beer));
            if (!tempList) {
                perror("内存扩容失败");
                free(beerList);
                fclose(fp);
                return 1;
            }
            beerList = tempList;
        }

        // 解析当前行到Beer结构体
        if (parseBeerLine(lineBuffer, &beerList[totalRows])) {
            totalRows++;
        } else {
            printf("解析第%zu行失败,跳过该行\n", totalRows+1);
        }
    }

    // 读取完成,输出结果验证
    printf("成功读取%zu行啤酒数据:\n", totalRows);
    for (size_t i = 0; i < totalRows; i++) {
        printf("第%zu行:名称=%s,酒精度=%.2f%%,生产年份=%d\n", 
               i+1, beerList[i].beerName, beerList[i].alcoholContent, beerList[i].productionYear);
    }

    // 清理资源
    free(beerList);
    fclose(fp);
    return 0;
}

关键要点说明

  • 动态扩容:用realloc在需要的时候扩展数组容量,避免预先分配过大的内存浪费,也不会因为行数不够导致溢出。
  • 文件指针重置:打开文件后如果有不确定的操作,调用rewind(fp)确保从文件开头读取。
  • 解析容错:添加解析失败的判断,避免一行出错导致整个程序崩溃。
  • 避免硬编码行数:完全不需要预先知道行数,程序会自动统计实际读取的有效行数。

内容的提问来源于stack exchange,提问作者David Hermansson

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 08:00:15