应使用std::getline逐行读取后按业务规则校验,再用状态机解析字段;非法行多因引号处理不当导致切分错误,须先校验再转换类型。

用 std::getline 逐行读取再判断,别碰正则或第三方CSV库
CSV不是结构化格式,C++标准库没有原生CSV解析器。强行用 std::stringstream 按逗号切分会崩在带引号、换行、转义逗号的字段里。真实业务中,非法行往往不是语法错误(如列数不对),而是语义违规——比如金额为负、日期早于2020年、ID重复。所以必须先完整读一行字符串,再按业务规则检查,合法才解析字段。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 用
std::getline从std::ifstream逐行读入std::string,不跳过任何字符(包括空行和纯空白行) - 对每行做轻量预检:空行、首字符是
#(注释)、长度超限(防恶意长行)直接跳过 - 业务校验逻辑写成独立函数,例如
bool is_valid_row(const std::string& line),返回false就continue - 别在读取时就调用
split或csv::parse—— 非法行根本不需要解析字段
字段分割必须用状态机,不能靠 find(',') + substr
CSV字段可能含逗号,只要被双引号包裹就算一个字段;引号本身还可能被转义("" 表示一个引号)。用简单字符串查找必然切错,导致列数错位、业务校验跑偏。你看到的“非法行”,八成是字段切歪后,把金额字段当成了字符串字段去比大小。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 手写一个极简状态机:三个状态(
OUTSIDE_QUOTE、INSIDE_QUOTE、AFTER_QUOTE),只关心引号和逗号 - 遇到未被引号包裹的逗号才切分;引号内的逗号一律忽略
- 发现
""要合并为单个",否则后续字符串比较会失败(比如校验"N/A"时多了一个引号) - 如果某行引号不成对,视为格式错误,直接标记为非法,不进业务逻辑
业务条件校验要放在字段解析后、数据转换前
字段切出来是字符串,但业务条件往往基于类型值:比如“年龄 > 0 且 int 再判断,遇到非数字字段(如空字符串、N/A)会触发 std::stoi 异常或返回 0,掩盖真实问题。更糟的是,有些字段允许空,但空值在业务上等价于默认值(如 discount=0.0),这些都要显式处理。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 先用
std::vector<:string></:string>存原始字段,不做任何转换 - 按字段索引查业务规则:第2列是金额 → 调用
parse_money(field[1]),该函数返回std::optional<double></double>,失败即非法 - 空字符串、全空白、
NULL、N/A等约定值,统一由解析函数识别并转为std::nullopt或默认值 - 所有数值比较前,确保
has_value()为真;否则整行丢弃
性能关键点:别让 std::string 反复拷贝
大CSV文件(百万行以上)里,每行都构造新 std::string、再切出一堆子串,内存分配开销远超业务逻辑本身。尤其当你用 substr 得到字段时,它默认深拷贝——哪怕你只是想比对前缀或检查是否为空。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 读行用
std::string line; line.reserve(4096);预分配,避免反复扩容 - 字段切分时,存
std::string_view(C++17)而非std::string,所有校验函数改用const std::string_view&入参 - 只有真正需要修改或长期持有字段内容时(比如存入容器),才调用
to_string() - 用
line.empty()和field.empty()判断空,别用== ""—— 前者是 O(1),后者可能触发临时对象构造
最易被忽略的是引号处理边界:状态机漏掉结尾引号、没处理连续两个引号、或者把字段末尾空格误判为分隔符。这些不会报错,但会让某几行“看似合法”实则字段错位,业务数据悄悄污染。上线前一定拿含引号、逗号、换行的真实样本测满三遍。



















