いずれ作らなければならないと思っていたものをついに作った。私の大学では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となり自由に使える分はない