7
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

UECAdvent Calendar 2019

Day 11

C++からCSVファイルを読み取る(クラス化)

Last updated at Posted at 2019-12-11

UECアドベントカレンダーの11日目の記事です。
思い付きで書いた短い記事ですいません。

昨日、C++からCSVファイルを1行ずつ読み取るプログラムを書きました。
しかし、よくよく考えてみると、ファイルを開く処理や閉じる処理、読み取り用バッファの準備はいかなるCSVファイルでも共通して必要な処理なので、クラス化した方がいいと思い、クラスにまとめてみました。
下の例のように、クラス化する前と後で、CSVファイルを出力するためのmain関数がだいぶ簡略化できることが分かります。

クラス化前

loadcsv.cpp
int main() {
    FILE* fp = fopen("data.csv", "r");
    if (fp == NULL) return 1;
    while (!feof(fp)) {
        char line[MaxLine];
        fgets(line, MaxLine, fp);
        CellsTy cells = cells_split(line, ',');
        if (cells.size() == 0) continue;

        // セルの出力処理
        for (string cell : cells) {
            cout << cell << "   ";
        }
        cout << endl;
    }
}

クラス化後

loadcsv.cpp
int main() {
	mycsv csv("data.csv");
	while (!csv.eof())
	{
		vector<string> cells = csv.loadnextline();
        // セルの出力処理
        for (string cell : cells) {
            cout << cell << "   ";
        }
		cout << endl;
	}
}

クラス化前と比べると、かなり読みやすくなったかと思います。

CSVファイルをC++で読み取るためのクラス

それでは、本題のコードです。
ファイルの入力にC言語を使っているところが若干気になりますが・・・。許してください。

loadcsv.cpp
#include <string>
#include <vector>
#include <iostream>
#include <cstdio>

using namespace std;

class mycsv {
private:
	FILE* fp;
	int bufsize;
	char* line;
	char comma;
	void init() {
		fp = NULL;
		line = new char[bufsize];
		comma = ',';
	}
public:
	// set comma charcter
	void setcomma(char c) {
		comma = c;
	}
	// open csv file
	bool open(string filename) {
		if (fp != NULL) fclose(fp);
		fp = fopen(filename.c_str(), "r");
		if (fp == NULL) return false;
		return true;
	}
	// close csv file
	void close() {
		if (fp != NULL) fclose(fp);
		fp = NULL;
	}
	vector<string> loadnextline() {
		memset(line, bufsize, sizeof(char) * bufsize);
		fgets(line, bufsize, fp);

		size_t len = strlen(line);
		line[len - 1] = comma;
		line[len] = '\0';

		vector<string> cells;
		string l;

		for (int i = 0; i < len; i++) {
			if (line[i] == '\r') continue;
			if (line[i] == comma) {
				cells.push_back(l);
				l.clear();
				continue;
			}
			l.push_back(line[i]);
		}
		return cells;
	}
	bool eof() {
		if (fp == NULL) return true;
		return feof(fp);
	}
	vector<vector<string>> loadall() {
		vector<vector<string>> ans;
		while (!eof())
		{
			ans.push_back(loadnextline());
		}
		return ans;
	}
	mycsv(string filename, int linemax = 4096) {
		bufsize = linemax;
		init();
		open(filename);
	}
	mycsv(int linemax) {
		bufsize = linemax;
		init();
	}
	~mycsv() {
		delete[] line;
		if (fp == NULL) return;
		fclose(fp);
		fp = NULL;
	}
};

// ここから下は使用例
int main() {
	mycsv csv("data.csv");
	while (!csv.eof())
	{
		vector<string> cells = csv.loadnextline();
		for (int i = 0; i < cells.size(); i++) {
			cout << cells[i] << "	";
		}
		cout << endl;
	}
}

メンバ関数

関数名 役割
open ファイルを開く
close ファイルを閉じる
loadnextline 1行読み込む
eof ファイルの終了まで読み込んだか否か
setcomma 区切り文字の変更
loadall csvファイルをすべて読み込み
mycsv コンストラクタ
~mycsv ディスコンストラクタ

コンストラクタ

コンストラクタは、次の3通りを作成しました。

  • 引数なし
  • ファイル名、1行あたりの最大文字数
  • 1行あたりの最大文字数

1行あたりの最大文字数を省略した場合、適当なサイズのバッファ(上記だと4096文字)を作成しています。
ファイル名を指定した場合、読み込みを行うようになっています。

open

ファイルを開く関数です。引数としてstring型の文字列を指定すると、ファイルを開きます。
コンストラクタで既に開いている場合は不要です。

close

ファイルを閉じる関数です。

loadnextline

1行分のセルを読み取る関数です。
vector<string>型で、各列に分解したものを返します。

eof

ファイルが読み込み可能かどうかを判定する関数です。falseであれば読み込めます。

setcomma

区切り文字を変更します。タブ区切りなどにしたくなった場合にどうぞ。

loadall

CSVファイルを一括で読み取ります。loadnextlineとは併用しないでください。
vector<vector<string>>型で、CSVファイル全体を返します。

ディスコンストラクタ

ファイルを閉じ、バッファを解放します。

7
4
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?