C++でのCSVファイルの処理について

色々あさってみて、最終的にこれが今一番しっくりきたので載せます。せっかくだし。

data.csv

10, 20, 30  
hello, world, !    
12a, 23b, 34c 

main.cpp

#include <iostream>
#include <fstream>
#include <string>
#include <vector>
using namespace std;

int main(){
    ifstream file("data.csv");
    vector<vector<string>> values;
    string str;
    int p;

    if(file.fail()){
        cerr << "failed." << endl;
        exit(0);
    }

    while(getline(file, str)){
        //コメント箇所は除く
        if( (p = str.find("//")) != str.npos ) continue;
        vector<string> inner;

        //コンマがあるかを探し、そこまでをvaluesに格納
        while( (p = str.find(",")) != str.npos ){
            inner.push_back(str.substr(0, p));

            //strの中身は", "の2文字を飛ばす
            str = str.substr(p+2);
        }
        inner.push_back(str);
        values.push_back(inner);
    }

    for(unsigned int i = 0; i < values.size(); ++i){
        for(unsigned int j = 0; j < values[i].size(); ++j){
            cout << values[i][j] << ",";
        }
        cout << endl;
    }

    return 0;
}

実行結果

10,20,30,  
hello,world,!,  
12a,23b,34c,  

という感じになります。
ちゃんと文字列も認識してくれてますね。

追記

さらにコードを整理しました。ただし、上記コードとは微妙に動作が違います。

#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
using namespace std;
 
vector<string> split(const string &str, const string &delim){
    vector<string> tokens;
    size_t cp, fp; // current position, found position
    const size_t dsize = delim.size();
    for(cp = 0; (fp = str.find(delim, cp)) != string::npos; cp = fp + dsize){
        tokens.emplace_back(str, cp, fp - cp);
    }
    tokens.emplace_back(str, cp, str.size() - cp);
    return tokens;
}

vector<vector<string>> parseTable(istream &stream, const string &delim){
    vector<vector<string>> table;
    string str;
    while(getline(stream, str)){
        table.push_back(split(str, delim));
    }
    return table;
}
 
vector<vector<string>> parseTable(const string &str, const string &delim){
    stringstream ss(str);
    return parseTable(ss, delim);
}
 
int main(){
    // stringをCSVとして解釈させる
    // ファイルから読み込む場合は、第1引数にファイルストリームを渡す
    // auto table = parseTable(ifstream("example.csv"), ",");
    auto table = parseTable("1,2,3\n4,5,6\n7,8,9", ",");
    for(auto& line : table){
        for(auto& cell : line){
            cout << cell << " ";
        }
        cout << endl;
    }
    cout << endl;
}

結果

1 2 3
4 5 6
7 8 9