Created
July 5, 2017 00:36
-
-
Save Tosainu/290aa71b0d8d92969b6fa53201702a7f to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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