Skip to content

Instantly share code, notes, and snippets.

@Tosainu
Created July 5, 2017 00:36
Show Gist options
  • Save Tosainu/290aa71b0d8d92969b6fa53201702a7f to your computer and use it in GitHub Desktop.
Save Tosainu/290aa71b0d8d92969b6fa53201702a7f to your computer and use it in GitHub Desktop.
#ifndef CSV_H
#define CSV_H
// 簡易的な型付きcsvパーサ
//
// const auto c = csv::parse<各列の型, ...>(入力ストリーム);
// のように呼び出すことで, 読み込んだ結果をtupleの配列として受け取ることができる.
#include <fstream>
#include <locale>
#include <sstream>
#include <stdexcept>
#include <tuple>
#include <type_traits>
#include <vector>
namespace csv {
// 入力ストリームからtupleのN番目に値を読み込む
// 読み込みに成功した場合はtrueを返す
template <class Stream, class Tuple, std::size_t N>
inline auto parse_line(Stream& ifs, Tuple& tuple)
// N番目がtupleの末尾であるとき(N+1がtupleのサイズと一致するとき),
// std::enable_if_tに与えた条件が満たされ, こちらの関数が呼ばれる
-> std::enable_if_t<(std::tuple_size<Tuple>::value == N + 1), bool> {
// N番目の要素に値を読み込む
// 最後の要素なのでこれ以上は読み込みを行わない
return static_cast<bool>(ifs >> std::get<N>(tuple));
}
template <class Stream, class Tuple, std::size_t N = 0u>
inline auto parse_line(Stream& ifs, Tuple& tuple)
// N番目がtupleの末尾でないとき(N+1がtupleのサイズと一致しないとき),
// std::enable_if_tに与えた条件が満たされ, こちらの関数が呼ばれる
-> std::enable_if_t<(std::tuple_size<Tuple>::value != N + 1), bool> {
// N番目の要素に値を読み込み, 続けてN+1番目の要素に値を読み込む
return (ifs >> std::get<N>(tuple)) && parse_line<Stream, Tuple, N + 1>(ifs, tuple);
}
// 入力ストリームのデリミタを変更するためのトリック
// http://en.cppreference.com/w/cpp/locale/ctype_char#Example
// ただし, Macのgccではうまく動作しないようなので若干変更を加えた.
// https://stackoverflow.com/questions/25130927/stdlocale-segfault-on-os-x-cannot-reproduce-on-any-other-platform/25131471
struct csv_whitespace : std::ctype<char> {
static const mask* make_table() {
static std::vector<mask> v(table_size, mask());
// static std::vector<mask> v(classic_table(), classic_table() + table_size);
v[','] |= space; // comma will be classified as whitespace
v[' '] &= ~space; // space will not be classified as whitespace
return &v[0];
}
csv_whitespace(std::size_t refs = 0) : ctype(make_table(), false, refs) {}
};
// csv形式のデータを読み込む
template <class... Types, class Stream>
std::vector<std::tuple<Types...>> parse(Stream& ifs) {
using result_type = std::vector<std::tuple<Types...>>; // 関数の戻り値の型
using value_type = typename result_type::value_type; // 1行のデータを格納するtupleの型
result_type result{};
// 引数に与えられた入力ストリームから1行ずつ読み込む
for (std::string s; std::getline(ifs, s);) {
// 読み込んだ文字列から文字列ストリームを作る
std::istringstream iss{s};
iss.imbue(std::locale(iss.getloc(), new csv_whitespace));
// 読み込んだ文字列を','で分割し, tupleに値を読み込む
value_type v{};
if (parse_line(iss, v)) {
// 読み込みに成功したら結果を格納する配列に追加
result.push_back(v);
} else {
// 失敗したら例外を投げる
throw std::runtime_error("invalid format");
}
}
return result;
}
} // namespace csv
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment