C++读取.arff文件有效数据行到结构体的实现疑问
解决ARFF文件有效数据读取问题
我来帮你搞定这个ARFF文件读取的问题!之前的实现没达到预期,大概率是跳过无关行的判断逻辑有漏洞,或者没正确遵循ARFF文件的结构规则。先理清楚ARFF的核心结构:
- 注释行:以
%开头(可能前后带空白) - 元数据行:以
@开头(比如@relation、@attribute),直到@data行之后才是有效数据 - 空行:包括全空白字符的行,都需要跳过
下面是完整的实现思路和代码示例(以C++为例,适配你的string类型结构体需求):
1. 定义目标结构体
首先定义存储数据的结构体,对应每行5个字符串项:
#include <string> struct ArffData { std::string item1; std::string item2; std::string item3; std::string item4; std::string item5; };
2. 核心读取逻辑
关键是精准过滤无关行,并正确分割有效数据项:
#include <iostream> #include <fstream> #include <vector> #include <sstream> #include <algorithm> #include <cctype> // 辅助函数:去除字符串前后的空白字符(空格、制表符等) std::string trim(const std::string& s) { auto start = s.begin(); while (start != s.end() && std::isspace(static_cast<unsigned char>(*start))) { start++; } auto end = s.end(); do { end--; } while (std::distance(start, end) > 0 && std::isspace(static_cast<unsigned char>(*end))); return std::string(start, end + 1); } std::vector<ArffData> readArffValidData(const std::string& filename) { std::vector<ArffData> dataCollection; std::ifstream arffFile(filename); if (!arffFile.is_open()) { std::cerr << "Error: 无法打开文件 " << filename << std::endl; return dataCollection; } std::string line; bool isInDataSection = false; // 标记是否进入@data之后的有效数据段 while (std::getline(arffFile, line)) { std::string trimmedLine = trim(line); // 规则1:跳过空行(trim后为空的行) if (trimmedLine.empty()) { continue; } // 规则2:跳过注释行(trim后以%开头) if (trimmedLine.front() == '%') { continue; } // 规则3:处理元数据行,找到@data后开启数据读取 if (trimmedLine.substr(0, 5) == "@data") { isInDataSection = true; continue; } // 规则4:未进入数据段时,跳过所有@开头的元数据行 if (!isInDataSection && trimmedLine.front() == '@') { continue; } // 现在处理有效数据行 if (isInDataSection) { std::stringstream lineStream(trimmedLine); std::string item; ArffData currentData; int itemIndex = 0; // 按逗号分割数据项(ARFF默认用逗号分隔) while (std::getline(lineStream, item, ',') && itemIndex < 5) { std::string trimmedItem = trim(item); switch (itemIndex) { case 0: currentData.item1 = trimmedItem; break; case 1: currentData.item2 = trimmedItem; break; case 2: currentData.item3 = trimmedItem; break; case 3: currentData.item4 = trimmedItem; break; case 4: currentData.item5 = trimmedItem; break; } itemIndex++; } // 确保读取到完整的5个数据项,再加入结果集合 if (itemIndex == 5) { dataCollection.push_back(currentData); } else { std::cerr << "Warning: 数据行格式错误,已跳过:" << line << std::endl; } } } arffFile.close(); return dataCollection; }
3. 关键细节解释
- trim函数:解决了行或数据项前后带空白的问题,避免因为
" % 注释"或" male "这类格式导致的判断/存储错误 - isInDataSection标记:严格遵循ARFF规范,只有在
@data行之后才开始读取有效数据,避免把@relation、@attribute等元数据行误判为有效数据 - 数据分割与校验:按逗号分割后,检查是否读取到5个数据项,过滤格式错误的行
4. 你之前可能踩的坑
- 没有处理行前后的空白:比如行开头有空格,直接判断
line.front()会拿到空格,导致无法识别注释/元数据行 - 没有等待
@data行就开始读取:ARFF文件的有效数据一定在@data之后,之前的所有@开头行都是元数据 - 空行判断只检查
line.empty():全是空格的行不会被判定为空,必须trim后再判断 - 数据项没有去空白:导致存储的字符串带多余空格,影响后续使用
内容的提问来源于stack exchange,提问作者PushedCrayon
相关产品推荐
相关产品推荐

