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