C++读取CSV至结构体数组报错:整数与字符数组读取失败
搞定CSV读取与二进制写入的坑
需求与现有问题
你需要读取CSV文件,将数据存入struct后写入二进制文件,CSV列对应:
- (int) 年份
- (int) 排名
- (char[]) 大学名称
- (float) 评分
- (char[]) 城市
- (char[]) 国家
示例CSV内容:
2018,1,Harvard University,97.7,Cambridge,United States 2018,2,University of Cambridge,94.6,Cambridge,United Kingdom 2018,3,University of Oxford,94.6,Oxford,United Kingdom 2018,4,Massachusetts Institute of Technology (MIT),92.5,Cambridge,United States 2018,5,Johns Hopkins University,92.1,Baltimore,United States
当前代码存在两个核心问题:整数读取受逗号干扰,调用getline读取字符数组时出现段错误,原代码如下:
struct Data { int year; int rank; char name[30]; float score; char city[30]; char country[30]; }; Data *universitie = new Data [5]; ifstream read ( "myFile.csv" ); int i = 0; while ( !read.eof() ) { read >> universitie[i].year; read >> universitie[i].rank; read.getline (universitie[i].name, 30, ','); //此处出现段错误 read >> universitie[i].score; read.getline (universitie[i].city, 30, ','); //此处出现段错误 read.getline (universitie[i].country, 30, '\n'); //此处出现段错误 i++; } read.close ();
注:因后续需写入二进制文件,必须使用char[30]而非string。
问题根源分析
- 整数读取残留分隔符:
>>运算符读取整数后,会将输入流中的逗号留在缓冲区,后续getline会直接读取这个逗号,导致读取内容为空或异常。 - 段错误触发原因:
- 残留的分隔符让
getline读取的内容不符合预期,甚至导致字符数组越界写入。 - 使用
!read.eof()作为循环条件是典型陷阱——EOF仅在读完最后一个字符后触发,读取失败后仍会继续循环,导致数组下标越界访问。
- 残留的分隔符让
修正后的完整代码
#include <fstream> #include <cstring> #include <limits> // 用于处理输入流的最大长度 using namespace std; struct Data { int year; int rank; char name[30]; float score; char city[30]; char country[30]; }; int main() { const int MAX_UNIVERSITIES = 5; Data* univs = new Data[MAX_UNIVERSITIES]; ifstream infile("myFile.csv"); int idx = 0; // 循环条件:未超出数组上限,且输入流状态正常 while (idx < MAX_UNIVERSITIES && infile) { // 读取年份,跳过后续所有字符直到逗号(兼容整数与逗号间的空格) infile >> univs[idx].year; infile.ignore(numeric_limits<streamsize>::max(), ','); // 读取排名,跳过后续逗号 infile >> univs[idx].rank; infile.ignore(numeric_limits<streamsize>::max(), ','); // 读取大学名称(到逗号为止),处理数组长度不足的情况 infile.getline(univs[idx].name, sizeof(univs[idx].name), ','); if (infile.fail() && !infile.eof()) { infile.clear(); // 清除输入流错误状态 infile.ignore(numeric_limits<streamsize>::max(), ','); // 跳过当前字段剩余内容 } // 读取评分,跳过后续逗号 infile >> univs[idx].score; infile.ignore(numeric_limits<streamsize>::max(), ','); // 读取城市(到逗号为止) infile.getline(univs[idx].city, sizeof(univs[idx].city), ','); if (infile.fail() && !infile.eof()) { infile.clear(); infile.ignore(numeric_limits<streamsize>::max(), ','); } // 读取国家(到换行符为止) infile.getline(univs[idx].country, sizeof(univs[idx].country)); if (infile.fail() && !infile.eof()) { infile.clear(); infile.ignore(numeric_limits<streamsize>::max(), '\n'); } idx++; } infile.close(); // 将结构体数组写入二进制文件 ofstream outfile("universities.bin", ios::binary); if (outfile) { outfile.write(reinterpret_cast<char*>(univs), sizeof(Data) * idx); outfile.close(); } delete[] univs; return 0; }
关键注意事项
- 正确处理整数后的分隔符:用
ignore(numeric_limits<streamsize>::max(), ',')替代单纯的ignore(),能跳过整数与逗号之间的所有字符(包括空格),确保后续getline读取到正确内容。 - 安全使用
getline读取字符数组:- 始终用
sizeof(数组名)指定读取长度,避免硬编码数值,防止数组长度修改后出现遗漏。 - 每次
getline后检查流状态,若因数组长度不足导致读取失败,需先清除错误状态,再跳过当前字段剩余内容,避免后续读取全部失效。
- 始终用
- 避免
!read.eof()的陷阱:直接用infile作为循环条件,它会同时检查EOF、错误、失败三种状态,再结合数组上限限制,彻底避免数组越界。 - 二进制写入的必要设置:打开文件时必须指定
ios::binary模式,防止Windows系统自动转换换行符,破坏二进制数据结构。
内容的提问来源于stack exchange,提问作者Étalo Alves Rabelo
相关产品推荐
相关产品推荐

