You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何单次遍历行字符串解析带双引号包裹的CSV文件

解析带双引号包裹字段的CSV文件解决方案

针对你遇到的三个核心问题,这里给出基于状态机的高效解析方案,直接解决行尾判断、字段间空白处理、字段内双引号转义的问题:

核心解决思路

采用状态机模式逐字符遍历,区分「非字段状态」和「字段内状态」,严格遵循CSV的引号转义规则:

  • 非字段状态:跳过空格和逗号,直到遇到字段起始引号
  • 字段内状态:处理普通字符,遇到连续双引号时保留单个,遇到单个双引号时结束当前字段

完整实现代码

#include <iostream>
#include <vector>
#include <string>
#include <cctype>

auto lines = std::vector<std::string>({
    R"("ab","cd")",          // 字段间无空格
    R"("ab", "cd")",         // 逗号后有空格
    R"("ab" ,"cd")",         // 逗号前有空格
    R"("a""b" ,"c""d")"      // 字段内包含转义双引号
});

auto parseQuotedCSVLine(const std::string& line, char wrapper) -> std::vector<std::string> {
    std::vector<std::string> chunks;
    std::string current_chunk;
    bool in_quoted_field = false;
    size_t i = 0;
    const size_t len = line.size();

    while (i < len) {
        char c = line[i];
        if (!in_quoted_field) {
            if (c == wrapper) {
                // 进入引号包裹的字段
                in_quoted_field = true;
                i++;
            } else if (c == ',' || std::isspace(static_cast<unsigned char>(c))) {
                // 字段外的分隔符或空白直接跳过
                i++;
            } else {
                // 忽略非引号包裹的字段(按需可扩展)
                i++;
            }
        } else {
            if (c == wrapper) {
                // 检查是否是转义双引号
                if (i + 1 < len && line[i+1] == wrapper) {
                    current_chunk += wrapper;
                    i += 2;
                } else {
                    // 当前字段结束,存入结果
                    chunks.push_back(current_chunk);
                    current_chunk.clear();
                    in_quoted_field = false;
                    i++;
                }
            } else {
                // 字段内普通字符直接追加
                current_chunk += c;
                i++;
            }
        }
    }

    // 处理未闭合的字段(可选逻辑)
    if (in_quoted_field && !current_chunk.empty()) {
        chunks.push_back(current_chunk);
    }

    return chunks;
}

void printChunks(const std::vector<std::string>& chunks) {
    for (const auto& s : chunks) {
        std::cout << s << std::endl;
    }
    std::cout << "---" << std::endl;
}

int main() {
    for (const auto& line : lines) {
        auto chunks = parseQuotedCSVLine(line, '"');
        printChunks(chunks);
    }
    return 0;
}

针对三个问题的具体处理

  1. 可靠判断行尾:
    遍历到字符串末尾时,若处于字段内状态,直接将当前未完成的字段存入结果(处理未闭合的异常情况);若处于非字段状态,说明所有字段已处理完毕。正常闭合的行中,最后一个字段的结束引号会触发状态切换,后续的空白字符会被跳过,自然完成行尾判断。

  2. 处理相邻字段间的空白:
    在非字段状态下,遇到空格、制表符等空白字符(通过std::isspace判断)直接跳过,仅在遇到字段起始引号时进入解析状态,彻底忽略字段间的冗余空白。

  3. 字段内双引号保留为单个:
    在字段内状态下,遇到引号时检查下一个字符:如果是连续的第二个引号,说明是CSV的转义规则,仅向当前字段添加一个引号并跳过下一个字符;如果是单个引号,则判定为字段结束。

测试输出

运行代码后,四个测试用例的输出依次为:

ab
cd
---
ab
cd
---
ab
cd
---
a"b
c"d
---

内容的提问来源于stack exchange,提问作者DEKKER

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 18:54:58