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

C语言读取大CSV文件无输出问题求助(附哈希表实现代码)

大CSV文件读取无输出问题排查与修复

我需要读取一个包含9列人员信息的CSV文件,将数据存入哈希表并实现按姓氏查询功能。当前代码在读取21条记录的小CSV时运行正常,但读取60000条记录的大CSV时完全没有输出,求问题原因及解决建议。

#include<stdio.h>
#include<stdlib.h>
#include <string.h>
#include <ctype.h>
#define tablesize 27
#define entries 21


unsigned long int collisions = 0;

typedef struct {
    char id[20];
    char depid[10];
    char surname[20];
    char forename[20];
    char age[2];
    char ptype[20];
    char gender[6];
    char nation[20];
    char religion[20];
    char occupation[20];
}dict;

dict* hashTable[tablesize]= {NULL};

unsigned long int hash_function(char* s){
    unsigned long int hash = 0;
    while(*s){
        hash = hash + *s;
        s++;
    }
    return hash%tablesize;
}

void print_table(){
    for(unsigned long int i=0;i<tablesize;i++){
        if(hashTable[i]==NULL){
            printf("%d\t---\t---\n",i);
        }
        else{
            printf("%d\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n",i,hashTable[i]->id,hashTable[i]->depid,hashTable[i]->surname,hashTable[i]->forename,hashTable[i]->age
            ,hashTable[i]->ptype,hashTable[i]->gender,hashTable[i]->nation,hashTable[i]->religion,hashTable[i]->occupation);
        }
    }
}

void insert(dict *d){
    unsigned long int ind = hash_function(d->surname);
    for(unsigned long int i=0;i<tablesize;i++){
        unsigned long int try = (ind+i)%tablesize;
        if(hashTable[try]==NULL){
            hashTable[try] = d;
            return;
        }
        else{
            collisions++;
        }
    }
}

void printvalues(unsigned long int i){
        printf("%d\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n",i,hashTable[i]->id,hashTable[i]->depid,hashTable[i]->surname,hashTable[i]->forename,hashTable[i]->age
        ,hashTable[i]->ptype,hashTable[i]->gender,hashTable[i]->nation,hashTable[i]->religion,hashTable[i]->occupation);
}

void search(char* name){
    unsigned long int ind = hash_function(name);
    unsigned long int f=1;
    for(unsigned long int i=0;i<tablesize;i++){
        unsigned long int try = (ind+i)%tablesize;
        if(hashTable[try]!=NULL&&strcmp(hashTable[try]->surname,name)==0){
            printvalues(try);
            f=0;
        }
    }
    if(f==1)
        printf("%s not in table\n",name);
    return;
}

int main(){
    FILE *fp = fopen("truncated.csv","r");
    if(!fp){
        printf("Error");
        return 0;
    }
    char buff[1024];
    unsigned long int row = 0, column = 0;
    dict values[entries];
    unsigned long int i=0;
    while(fgets(buff,1024,fp)){
        column=0;
        row++;
        if(row==1){
            continue;
        }
        char *field = strtok(buff,",");
        while(field){
            if(column==0){
                strcpy(values[i].id,field);
            }
            if(column==1){
                strcpy(values[i].depid,field);
            }
            if(column==2){
                strcpy(values[i].surname,field);
            }
            if(column==3){
                strcpy(values[i].forename,field);
            }
            if(column==4){
                strcpy(values[i].age,field);
            }
            if(column==5){
                strcpy(values[i].ptype,field);
            }
            if(column==6){
                strcpy(values[i].gender,field);
            }
            if(column==7){
                strcpy(values[i].nation,field);
            }
            if(column==8){
                strcpy(values[i].religion,field);
            }
            if(column==9){
                strcpy(values[i].occupation,field);
            }
            field = strtok(NULL,",");
            column++;
        }
        i++;
    }
    fclose(fp);
    for(unsigned long int i=0;i<entries;i++){
        insert(&values[i]);
    }
    //printvalues(values);
    //print_table();
    while(1){
        printf("Enter term to get frequency or type 'quit' to escape:");
        char name[20];
        scanf("%s",name);
        if(strcmp(name,"quit")==0)
            return 0;
        search(name);
    }
    return 0;
}

问题原因

  • 栈内存溢出:代码中用dict values[entries];在栈上分配固定大小数组,entries仅为21。处理60000条记录时,i会远超21,导致越界写入栈内存,破坏程序运行环境,引发崩溃或无响应。
  • 哈希表容量严重不足:tablesize仅为27,插入60000条数据时,哈希表会被快速填满,insert函数的线性探测循环会遍历整个表却找不到空位,陷入无效循环或因栈溢出提前终止。
  • CSV解析鲁棒性差:strtok无法处理带引号的CSV字段,且未处理行尾换行符,可能导致字段内容异常;同时未校验列数,若CSV格式不符会引发字段越界。
  • 错误处理缺失:大文件读取时可能出现IO错误,但代码未完整检查fgets的返回状态,无法及时发现读取异常。

解决建议

  • 改用动态内存存储记录:放弃栈上固定数组,用malloc/realloc动态扩容存储数据,避免栈溢出:
    dict* values = NULL;
    unsigned long int record_count = 0;
    
    // 读取每条记录后
    values = realloc(values, (record_count + 1) * sizeof(dict));
    if (!values) {
        fprintf(stderr, "Memory allocation failed\n");
        exit(EXIT_FAILURE);
    }
    // 填充values[record_count]的字段
    record_count++;
    
  • 扩容哈希表并优化哈希函数:
    • 将tablesize设置为远大于60000的质数(如65537),大幅降低碰撞概率。
    • 替换简单累加的哈希函数为更高效的DJB2实现:
      unsigned long int hash_function(char* s){
          unsigned long int hash = 5381;
          int c;
          while ((c = *s++))
              hash = ((hash << 5) + hash) + c; // hash * 33 + c
          return hash % tablesize;
      }
      
  • 改用链地址法处理哈希冲突:线性探测在高负载下效率极低,改用链表存储每个哈希桶的冲突元素,确保插入正常:
    typedef struct dict_node {
        dict data;
        struct dict_node* next;
    } dict_node;
    
    dict_node* hashTable[tablesize] = {NULL};
    
    void insert(dict *d){
        unsigned long int ind = hash_function(d->surname);
        dict_node* new_node = malloc(sizeof(dict_node));
        if (!new_node) return;
        memcpy(&new_node->data, d, sizeof(dict));
        new_node->next = hashTable[ind];
        hashTable[ind] = new_node;
    }
    
  • 修复CSV解析逻辑:
    • 替换strtok为支持带引号字段的CSV解析逻辑,处理换行符和特殊字符。
    • 读取每条记录后校验column是否等于9,避免字段越界。
    • 用strcspn去除行尾换行符:buff[strcspn(buff, "\n")] = '\0';
  • 完善错误检查:
    • 检查fopen、malloc、realloc的返回值,输出明确错误信息。
    • 大文件读取时添加进度提示,确认程序运行状态。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 13:35:31