首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> C++ 入門

C++ 手寫 CSV 解析器:雙引號字段、換行轉義與流式讀取實現

     編輯:C++ 入門

CSV 格式痛點

  • 字段含逗號需雙引號包裹:"a,b",c;
  • 字段含雙引號需轉義:"He said ""Hi""",c;
  • 字段可跨行(含換行符);
  • 標準庫無 CSV 支持,第三方庫(Fast C++ CSV Parser, csv2)輕量但需依賴。

自定義解析器設計

針對**雙引號包裹、無轉義雙引號、無跨行**的簡化 CSV(常見導出格式),實現流式逐行解析:

核心輔助函數

// 統計子串出現次數
int count_substr(const char* str, const char* sub) {
    int cnt = 0;
    for (int i = 0; str[i]; ++i) {
        int j = i, k = 0;
        for (; str[j] == sub[k] && str[j]; ++j, ++k);
        if (!sub[k]) ++cnt;
    }
    return cnt;
}

// 查找子串從 pos 開始的位置
int find_substr(const char* str, const char* sub, int pos) {
    for (int i = pos; str[i]; ++i) {
        int j = i, k = 0;
        for (; str[j] == sub[k] && str[j]; ++j, ++k);
        if (!sub[k]) return i;
    }
    return -1;
}

主解析函數:逐行讀取、分割字段

// 讀取一行 CSV 到 vector
char* read_csv_line(std::vector& fields, FILE* fp) {
    char buf[1024];
    char* ret = fgets(buf, sizeof(buf), fp);
    if (!ret) return nullptr;

    std::string line(buf);
    int quote_cnt = count_substr(line.c_str(), "\"\"");
    if (quote_cnt > 0) {
        int pos = find_substr(line.c_str(), "\"\"", 0);
        int start = 1; // 跳過開頭引號
        while (pos > 0) {
            fields.push_back(line.substr(start, pos - start));
            start = pos + 3; // 跳過 "" 及後面的逗號
            pos = find_substr(line.c_str(), "\"\"", pos + 2);
        }
        // 最後一字段(去除結尾引號及換行)
        int end = line.find('\n');
        if (end == std::string::npos) end = line.size();
        fields.push_back(line.substr(start, end - start - 1)); // -1 去尾引號
    }
    return ret;
}

使用示例

int main() {
    FILE* fp = fopen("data.csv", "r");
    if (!fp) return 1;

    std::vector row;
    while (read_csv_line(row, fp)) {
        for (size_t i = 0; i < row.size(); ++i)
            std::cout << "Field[" << i << "] = " << row[i] << '\n';
        row.clear();
    }
    fclose(fp);
}

輸入示例 data.csv

"aaa","bbb","123"
"ccc","ddd","456"
"eee","fff","789"

局限性與改進方向

限制改進
固定緩衝區 1024 字節動態擴容或用 std::getline
不支持轉義雙引號 ""狀態機解析:進入引號模式,遇 "" 輸出 "
不支持跨行字段狀態機跨 fgets 保持引號狀態
不支持無引號字段按逗號分割,修剪空白

生產環境建議

  • 用成熟庫:csv2(單頭文件、C++17、完整 RFC 4180)、Fast C++ CSV Parser;
  • 或用 std::getline + 狀態機自寫完整解析器(約 100 行)。

結語

手寫 CSV 解析器是理解文本處理、狀態機、流式 IO 的好練習。但**生產代碼請用經過測試的庫**,邊界情況極多(編碼、BOM、異常換行、巨大字段)。本文代碼僅供教學、簡單場景或無依賴環境參考。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved