こんにちは。
現役エンジニアの”はやぶさ”@Cpp_Learningです。
C++でCSVファイルを読むときに便利なライブラリ”fast-cpp-csv-parser”を見つけたので、勉強しながら色々なサンプルコードを作成しました。
備忘録も兼ねて本記事を書きます。
Contents
CSVとは
CSVとは、カンマ「 , 」で区切ったテキストデータのことです。本記事で使用するcsvファイル”test.csv”は以下の通り。
name, height, weight, label
Hayabusa, 1.7, 63, 1
Kururu, 0.2, 0.19, 1
Pocha, , 1.1, 2
Ururu, 0.3, 0.9, 2
はやぶさ(Hayabusa), フクロウのくるる(Kururu), フェレットのぽちゃ(Pocha), フェレットのうるる(Ururu)の身長・体重・ラベルのデータです。
※我が家に住み着いている可愛い動物たち。可愛い写真をもっと見たい人は以下のサイトへGo!
VSCode拡張機能 -Rainbow CSV-
ExcelでCSVファイルを読み書きする人が多いと思いますが、私はVisual Studio Codeの拡張機能”Rainbow CSV”を愛用しています。
Rainbow CSVを使えば、先ほど紹介した”test.csv”が以下のように綺麗に表示されます。
Rainbow CSVの使い方や魅力については、以下の記事で丁寧に説明しています。
ここから本番!
Fast C++ CSV Parserの基本的な使い方
fast-cpp-csv-parser|GitHubは”ヘッダーオンリーなライブラリ”なので、”csv.h”をインクルードするだけで使うことができます。
fast-cpp-csv-parserのシンプルな使い方は以下の通りです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
#include <iostream> #include "csv.h" // #define COLUMNS 4 constexpr int COLUMNS = 4; /* ======== test.csv ========= name, height, weight, label hayabusa, 1.7, 63, 1 kururu, 0.2, 0.19, 1 Pocha, , 1.1, 2 Uururu, 0.3, 0.9, 2 =========================== */ int main(){ std::string name; double height; double weight; int label; io::CSVReader<COLUMNS> in("test.csv"); in.read_header(io::ignore_extra_column, "name", "height", "weight", "label"); while(in.read_row(name, height, weight, label)){ std::cout << "Name: " << name << std::endl; std::cout << "Height: " << height << " [m]" << std::endl; std::cout << "Weight: " << weight << " [kg]" << std::endl; std::cout << "Label: " << label << std::endl; std::cout << std::endl; } } |
順番に解説していきます。
Fast C++ CSV Parserの解説
最初に各カラムの型を定義します。
1 2 3 4 |
std::string name; double height; double weight; int label; |
次に読み込みたいCSVファイルとカラム数を設定します。
1 2 3 |
io::CSVReader<4> in("test.csv"); |
続いてヘッダー情報を設定します(カラム数を”4”と設定したので、カラムを4つセット)
1 2 3 |
in.read_header(io::ignore_extra_column, "name", "height", "weight", "label"); |
以下のコードで1行ずつパラメータを取得し、任意の処理を行います。
1 2 3 |
while(in.read_row(name, height, weight, label)){ // do stuff with the data } |
今回は、”test.csv”の全情報をターミナルに表示させてみます。
1 2 3 4 5 6 7 |
while(in.read_row(name, height, weight, label)){ std::cout << "Name: " << name << std::endl; std::cout << "Height: " << height << " [m]" << std::endl; std::cout << "Weight: " << weight << " [kg]" << std::endl; std::cout << "Label: " << label << std::endl; std::cout << std::endl; } |
ビルドと動作確認
最初に紹介したソースコードを”sample.cpp”と名付け、以下のコマンドでビルドします。
g++ -std=c++0x sample.cpp -o sample -lpthread
“sample”という実行ファイルができるので、以下のコマンドで実行します。
./sample
一連の流れを実施したときのターミナル画面は以下の通りです。
以下の記事で紹介したWSL(Windows Subsystem for Linux)で動作確認しています。
※C++11がビルドできる環境ならWindows/Linuxどちらでも動きます。
Fast C++ CSV Parserの応用
以降から応用編です。
カラムを指定して抽出
指定したカラムの情報のみを抽出することもできます。例えば、以下のコードでnameとheightのみを抽出できます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
#include <iostream> #include "csv.h" /* ======== test.csv ========= name, height, weight, label hayabusa, 1.7, 63, 1 kururu, 0.2, 0.19, 1 Pocha, , 1.1, 2 Uururu, 0.3, 0.9, 2 =========================== */ int main(){ std::string name; double height; io::CSVReader<2> in("test.csv"); in.read_header(io::ignore_extra_column, "name", "height"); while(in.read_row(name, height)){ std::cout << "Name: " << name << std::endl; std::cout << "Height: " << height << " [m]" << std::endl; std::cout << std::endl; } } |
先ほど同様にビルドおよび実行した結果が以下です。
※ぽちゃ(Pocha)の身長は欠損データですが”0″で補間されます
行数を取得する
以下のコードで行数を取得することができます。
1 2 3 |
in.get_file_line() |
ただし、ヘッダー(カラム名の部分)から1行目とカウントするので、以下のコード(23行目)のように”-1”すると良いです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
#include <iostream> #include "csv.h" constexpr int COLUMNS = 3; /* ======== test.csv ========= name, height, weight, label hayabusa, 1.7, 63, 1 kururu, 0.2, 0.19, 1 Pocha, , 1.1, 2 Uururu, 0.3, 0.9, 2 =========================== */ int main(){ std::string name; double height; double weight; io::CSVReader<COLUMNS> in("test.csv"); in.read_header(io::ignore_extra_column, "name", "height", "weight"); while(in.read_row(name, height, weight)){ std::cout << "No." << in.get_file_line() - 1 << std::endl; std::cout << "Name: " << name << std::endl; std::cout << "Height: " << height << " [m]" << std::endl; std::cout << "Weight: " << weight << " [kg]" << std::endl; std::cout << std::endl; } } |
実行結果は以下の通り。
指定行の情報を抽出
指定した行のみを抽出することもできます。例えば、以下のコードで2~3行目を抽出できます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
#include <iostream> #include "csv.h" constexpr int COLUMNS = 3; /* ======== test.csv ========= name, height, weight, label hayabusa, 1.7, 63, 1 kururu, 0.2, 0.19, 1 Pocha, , 1.1, 2 Uururu, 0.3, 0.9, 2 =========================== */ int main(){ std::string name; double height; double weight; int start_row = 2; int end_row = 3; int rows = 0; io::CSVReader<COLUMNS> in("test.csv"); in.read_header(io::ignore_extra_column, "name", "height", "weight"); while(in.read_row(name, height, weight)){ rows = in.get_file_line()-1; // get 2~3 row if(start_row <= rows && rows <= end_row){ std::cout << "No." << in.get_file_line() - 1 << std::endl; std::cout << "Name: " << name << std::endl; std::cout << "Height: " << height << " [m]" << std::endl; std::cout << "Weight: " << weight << " [kg]" << std::endl; std::cout << std::endl; } } } |
実行結果は以下の通り。
条件付きで情報抽出
身長制限などの条件付きで情報を抽出することもできます。例えば、以下のコードでラベルが”2”の動物のみ抽出できます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
#include <iostream> #include "csv.h" constexpr int COLUMNS = 4; /* ======== test.csv ========= name, height, weight, label hayabusa, 1.7, 63, 1 kururu, 0.2, 0.19, 1 Pocha, , 1.1, 2 Uururu, 0.3, 0.9, 2 =========================== */ int main(){ std::string name; double height; double weight; int label; io::CSVReader<COLUMNS> in("test.csv"); in.read_header(io::ignore_extra_column, "name", "height", "weight", "label"); while(in.read_row(name, height, weight, label)){ // get Label:2 line if(label == 2){ std::cout << "Name: " << name << std::endl; std::cout << "Height: " << height << " [m]" << std::endl; std::cout << "Weight: " << weight << " [kg]" << std::endl; std::cout << "Label: " << label << std::endl; std::cout << std::endl; } } } |
実行結果は以下の通り。
コードの25行目を好きな条件式に変更して下さい
ベクトルに代入
後処理で扱いやすいように、抽出した情報をベクトル(std::vector)に代入します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
#include <iostream> #include <vector> #include "csv.h" constexpr int COLUMNS = 4; /* ======== test.csv ========= name, height, weight, label hayabusa, 1.7, 63, 1 kururu, 0.2, 0.19, 1 Pocha, , 1.1, 2 Uururu, 0.3, 0.9, 2 =========================== */ int main(){ std::string name; double height; double weight; int label; std::vector<std::string> vec_name; std::vector<double> vec_weight; io::CSVReader<COLUMNS> in("test.csv"); in.read_header(io::ignore_extra_column, "name", "height", "weight", "label"); // create vector while(in.read_row(name, height, weight, label)){ vec_name.push_back(name); vec_weight.push_back(weight); } std::cout << "====== vec_name ======" << std::endl; for(auto names: vec_name){ std::cout << names << std::endl; } std::cout << "===== vec_weight =====" << std::endl; for(auto weights: vec_weight){ std::cout << weights << std::endl; } } |
実行結果は以下の通り。
まとめ
C++でCSVファイルを読むときに便利なライブラリ”fast-cpp-csv-parser”の使い方について、複数のサンプルコードを交えて説明しました。
C++で手軽にCSVファイルを扱いたい人にオススメのライブラリですので、是非使ってみて下さい。