4
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

posted at

updated at

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

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ファイル全体を返します。

ディスコンストラクタ

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

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
4
Help us understand the problem. What are the problem?