c++如何读取csv文件数据_c++解析csv代码【源码】

12次阅读

最稳妥方式是用std::getline按行读取后手动实现CSV状态机解析:区分unquoted/quoted/escaped_quote三态,正确处理引号转义、嵌套逗号及换行,避免直接用逗号分隔或operator>>破坏结构。

c++如何读取csv文件数据_c++解析csv代码【源码】

std::ifstream 逐行读取 csv 最稳妥

CSV 不是标准格式,没有统一规范,所以别指望有“万能解析库”——c++ 标准库本身也不提供 CSV 解析。最可控的方式是手动按行读取,再用简单分隔逻辑拆字段。关键不是“多快”,而是“不误判换行、引号、逗号”。

常见错误现象:std::getline 直接用 ',' 当分隔符会崩,因为字段里可能含逗号(如 "Smith, John",25,"New York");用 operator>> 会跳过空格和换行,彻底破坏结构。

  • 始终用 std::getline(in, line) 按行读,保留原始换行信息
  • 对每一行,从左到右扫描,遇双引号就进入“quoted mode”,跳过内部的逗号和换行
  • 字段间以非 quoted 状态下的逗号为界,行尾也算一个字段结束点
  • 记得处理 rnwindows)和 nunix)兼容性,std::getline 默认按 n 切,一般够用

处理带引号的字段必须自己写状态机

标准 CSV 规则(RFC 4180)规定:字段含逗号、换行或双引号时,必须用双引号包裹;双引号本身用两个双引号表示("")。想靠 std::String::find + substr 硬切,十次有九次漏掉嵌套引号或转义。

实操建议:写一个极简状态机,三个状态足矣:unquotedquotedescaped_quote。每读一个字符就更新状态和当前字段内容。

立即学习C++免费学习笔记(深入)”;

示例片段(核心逻辑):

std::vector fields; std::string field; bool in_quotes = false; for (char c : line) {     if (!in_quotes && c == ',') {         fields.push_back(std::move(field));         field.clear();     } else if (c == '"') {         if (in_quotes && line.substr(pos, 2) == """") {             field += '"';             pos++; // skip next quote         } else {             in_quotes = !in_quotes;         }     } else {         field += c;     } } fields.push_back(std::move(field));

别用 std::stoi/std::stod 直接转数值字段

CSV 字段可能是空、全是空格、或非法数字(如 "N/A"),直接调 std::stoi 会抛 std::invalid_argument 异常,程序崩溃。生产代码必须做前置校验。

  • 先用 field.find_first_not_of(" t") 检查是否为空白
  • 再用 std::from_chars(C++17 起)替代 std::stoi:它不抛异常,返回 std::errc::invalid_argument 错误码,且不跳过尾部垃圾字符(比如 "123abc" 不该算合法整数)
  • 如果字段是日期、布尔等类型,别硬转——先存 std::string,后续按需解析,避免早期失败

内存与性能:小文件用 vector,大文件考虑流式处理

读完整个 CSV 再解析,对几百 MB 的文件就是灾难:内存暴涨,且无法及时响应部分数据。但也不是所有场景都要上 mmap 或异步 IO。

  • 若单行不超过几 KB、总行数 std::vector<:vector>> 完全 OK
  • 若要边读边处理(如导入数据库、实时统计),把解析逻辑封装成回调:每次成功提取一行,立刻调用 process_row(const std::vector<:string>&)
  • 避免在循环里反复 new/deletestd::string 频繁重分配:复用 fieldfields 容器,调用 .clear() 而非重建

实际最难的部分不是读,而是定义“这个字段到底算什么类型”——CSV 没 schema,同一列前 100 行是数字,第 101 行突然变成字符串,这种情况必须提前约定规则,或留好 fallback 路径。

text=ZqhQzanResources