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
相关产品推荐
相关产品推荐

