C++ でCSVファイルを扱う

#はじめに
研究で数値シミュレーションをしているのですが，C++ でCSVファイルを扱うためのライブラリで良いなと思うものがなかなか無かったので，書いたものを共有しておきます．記述に一貫性が無いことや，おそらく実行速度が速くはないことなど，問題は残っているので適宜更新していこうと思います．

#やりたいこと
ヘッダーやインデックスの有無に対応した，CSVファイル取得用のクラスを作る．各種データ型に対応させる．今回の実装は，Header と Index は string のみ，Cell は int，double，string の３つを行う．それぞれ vector で管理し，まとめて入出力できるようにする．
![csv.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/511606/201a8945-d803-ced6-d1a9-04a20d719a89.png)

#スクリプト
CSVHandler をmainファイルにインクルードして利用します．

```CSVHandler.h
#pragma once
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <sstream>
using namespace std;

template <typename T>
class CSVFile {
public:
	string filepath;
	bool isHeader;
	bool isIndex;
	char delim;
	// ヘッダー名保管
	vector<string> header;
	// インデックス名保管
	vector<string> index;
	// 要素保管
	vector<vector<T>> cell;

	// コンストラクタ関数
	CSVFile() {
		isHeader = false;
		isIndex = false;
		delim = ',';
	}
	T cast_cell(string);
	void csv_read(string, bool, bool, char);
	void csv_write(string, char);
	void csv_show();
};

// cell の型に応じたキャスト処理の定義
int CSVFile<int>::cast_cell(string str) {
	return stoi(str);
}
double CSVFile<double>::cast_cell(string str) {
	return stod(str);
}
string CSVFile<string>::cast_cell(string str) {
	return str;
}

// CSVファイルを読みこむ
template <typename T>
void CSVFile<T>::csv_read(string filepath, bool isHeader, bool isIndex, char delim) {
	this->filepath = filepath;
	this->isHeader = isHeader;
	this->isIndex = isIndex;
	this->delim = delim;

	string str_buf;
	string str_comma_buf;

	// 読み込むcsvファイルを開く(ifstreamのコンストラクタで開く)
	ifstream ifs_csv_file(filepath);

	// getline関数で1行ずつ読み込む(読み込んだ内容は str_buf に格納)
	for (int i = 0; getline(ifs_csv_file, str_buf); i++) {
		cell.push_back(vector<T>());

		// delim 区切りごとにデータを読み込むために istringstream にする
		istringstream i_stream(str_buf);

		for (int j = 0; getline(i_stream, str_comma_buf, delim); j++) {
			if (isHeader && isIndex) {
				if (i == 0 && j == 0) continue;
				if (i == 0 && j != 0) header.push_back(str_comma_buf);
				if (i != 0 && j == 0) index.push_back(str_comma_buf);
				if (i != 0 && j != 0) cell.at(i - 1).push_back(cast_cell(str_comma_buf));
			}
			else if (isHeader) {
				if (i == 0) header.push_back(str_comma_buf);
				if (i != 0 && j == 0) index.push_back(string());
				if (i != 0) cell.at(i - 1).push_back(cast_cell(str_comma_buf));
			}
			else if (isIndex) {
				if (i == 0 && j != 0) header.push_back(string());
				if (j == 0) index.push_back(str_comma_buf);
				if (j != 0) cell.at(i).push_back(cast_cell(str_comma_buf));
			}
			else {
				if (i == 0) header.push_back(string());
				if (j == 0) index.push_back(string());
				cell.at(i).push_back(cast_cell(str_comma_buf));
			}
		}
	}
}

// CSVファイルをファイル出力する
template <typename T>
void CSVFile<T>::csv_write(string filepath, char delim) {
	// 書き込むcsvファイルを開く(ofstreamのコンストラクタで開く)
	ofstream ofs_csv_file(filepath);

	if (isHeader) {
		for (int j = 0; j < header.size(); j++) {
			if (isIndex && j == 0) ofs_csv_file << delim;
			ofs_csv_file << header.at(j) << delim;
		}
		ofs_csv_file << endl;
	}
	for (int i = 0; i < index.size(); i++) {
		if (isIndex) {
			ofs_csv_file << index.at(i) << delim;
		}
		for (int j = 0; j < header.size(); j++) {
			ofs_csv_file << cell.at(i).at(j) << delim;
		}
		ofs_csv_file << endl;
	}
	ofs_csv_file << endl;
}

// CSVファイルをコンソール出力する
template <typename T>
void CSVFile<T>::csv_show() {
	cout << "filepath = " << filepath << ", ";
	cout << "isHeader = " << int(isHeader) << ", ";
	cout << "isIndex = " << int(isIndex) << ", ";
	cout << "delim = " << delim << endl;
	cout << "header size = " << header.size() << ", ";
	cout << "index size = " << index.size() << endl;

	if (isHeader) {
		for (int j = 0; j < header.size(); j++) {
			if (isIndex && j == 0) cout << "\t";
			cout << header.at(j) << "(h)" << "\t";
		}
		cout << endl;
	}
	for (int i = 0; i < index.size(); i++) {
		if (isIndex) {
			cout << index.at(i) << "(i)" << "\t";
		}
		for (int j = 0; j < header.size(); j++) {
			cout << cell.at(i).at(j) << "\t";
		}
		cout << endl;
	}
	cout << endl;
}
```

```main.cpp
#include "CSVHandler.h"

int main() {
	// CSVFile<型名> data;
	CSVFile<int> data;
	// data.csv_read(入力ファイル名, ヘッダーの有無, インデックスの有無, 区切り文字);
	data.csv_read("data.csv", true, true, ',');
	//要素を全て２倍する
	for (int i = 0; i < data.cell.size(); i++)
		for (int j = 0; j < data.cell.at(i).size(); j++)
			data.cell.at(i).at(j) *= 2;
	// data.csv_write(出力ファイル名, 区切り文字);
	data.csv_write("output.csv", ',');
	// data.csv_show();
	data.csv_show();
}
```
入力ファイル data.csv
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/511606/2f83fe45-6b62-063d-173c-5ba6e8ca6b71.png)

出力ファイル output.csv
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/511606/1645efe8-6652-55c9-5a64-c4457fa6c317.png)


コンソール出力

```
filepath = data.csv, isHeader = 1, isIndex = 1, delim = ,
header size = 5, index size = 3
        a(h)    b(h)    c(h)    d(h)    e(h)
1(i)    2       4       6       8       10
2(i)    12      14      16      18      20
3(i)    22      24      26      28      30
```

#修正したい箇所
CSVFile のメンバ変数を全て public で定義していますが，本来は private で定義すべきだと思います．csv_read と，csv_write と csv_show で要素へのアクセス順序が違っていて一貫性が無いので，修正したいです．また，対応する型も増やしたいです．

#参考
参考にしたサイトもまとめておきます．
https://qiita.com/shirosuke_93/items/d5d068bb15c8e8817c34