tokenizerとcsvパーサを作った。

いずれ作らなければならないと思っていたものをついに作った。私の大学では2年でC++をやる。C++erの私としては非常にうれしいことである。ただ、大学のPCはBoostが入らない*1ので、自分でBoostのような汎用ライブラリを作る以外に手はない。

知り合いの先輩から入手した情報によると少なくとも2年前からcsvをパースする課題が出ているようだ。これはもちろんboost::tokenizerを使えば一瞬だが、先の理由によりBoostは使えない。仕方ないのでcsvパーサを作ることにしたのだ。

私は1年前からouchilibという名前の汎用ライブラリを作っている。コードの墓場という表現の方が正しいかもしれないが、まあまあ使えるものが多い。今回はそいつにtokenizerとcsvパーサを追加すべくちゃんと書いた。

https://github.com/ouchiminh/ouchilib/tree/master/include/ouchilib/tokenizer

tokenizer

tokenizer君は本題ではないのでパパっと終わらせたい。tokenizer君は文字列をワードとセパレータの二種類に分ける。後から出てくるcsv_parser君はその中からワードだけを取り出すという要領だ。

using namespace ouchi::tokenizer;
separator<char> sep{ ",\t" };  // separatorの登録。','と'\t'をセパレータとして処理する。
tokenizer<char> tok{ "csv,csv,csv", sep };
for(auto[type, str] : tok){
    std::cout << str << '\n';
}
/** output
csv
,
csv
,
csv
**/

最低限の機能しか持たないが、これだけでも非常に役に立つ。

csv_parser

次のcsv_parserも最低限の機能以外は持たない。やることはさっきのtokenizerの出力を見て二次元配列にワードをぶち込むことだけだ。 以下のようなcsvファイルdata.csvがあるとき、

a,b,c,d
,,,target
std::ifstream fs("data.csv");
ouchi::parser::csv<char> pcsv;
pcsv.parse(fs);
std::cout << pcsv.at("d", 1) << '\n';  // 出力はtarget

このようにしてcsvのデータを取得できる。尚、parse()はパースに失敗した場合例外を投げる。

at()には行, 列の順ではなく、x, yの順でキーを指定する。キーは文字列または数値だ。

ここに一つの巨大なcsvファイルがある。気象庁が発表している毎年の地方ごとの梅雨入り, 梅雨明けの日にちの統計データだ。

https://www.data.jma.go.jp/fcd/yoho/baiu/tsuyu_iriake.csv

関東甲信地方の2000年の梅雨入りの月がしりたいときは以下のようなコードになる。

std::ifstream fs("tsuyu_iriake.csv");
ouchi::parser::csv<char> pcsv;
pcsv.parse(fs);

std::cout << pcsv.at(u8"関東甲信地方", u8"2000") << '\n';

*1:学生が使えるストレージ容量は1GBしかない。様々なコンフィグファイルやアドオンなどを差し引いたら数百MBとなり自由に使える分はない