汎用的な CSV リーダーを作ってみた
いやまあ、もうこんなの巷にいくらでも溢れてるだろうけど。
こいつの特徴は、どのヘッダにも依存しないってこと。 iterator_traits のために
テンプレート引数をうまく指定してやれば何でも出来る!でも逆に言えば単体では使い物にならないっていう。
やろうと思えば UTF-8 のデータを読んで UTF-16 の文字列を構築するとかもできるはず。
それぞれのテンプレート引数には何が必要なのかとかは、ソース見て判断してくだしぃ。
参考:中の技術日誌 わんくまライブラリ Wankuma.IO.CSVReaderクラス Version1
[2010/5/27 2:45 更新]
- 入力をInputIteratorにしました。なんかC++っぽくなった気がする。
- この CSV リーダ自体をイテレータっぽく扱えるようにしてみました。
- ただ row::begin() を呼び出すたびに read_column() するので、reader3 の例みたいなへんてこな動きをします。
- basic_csv_reader にカラム1個分先読みしたのを保持させればいいんでしょうけど、最初の例みたいに使える以上そんなオーバーヘッドを付けるのは嫌なので、これは仕方ないかなぁという気も。
[2010/6/4 2:17 更新]
#include <iterator> template<class InputIterator, class SpecialCharTraits> class basic_csv_reader { public: typedef InputIterator input_iterator; typedef typename std::iterator_traits<InputIterator>::value_type char_type; typedef SpecialCharTraits sp_traits; private: InputIterator it_; InputIterator end_; char_type next_; bool eol_; // end of line char_type next_char() { char_type c(next_); if (++it_ != end_) next_ = *it_; return c; } template<class OutputIterator> void construct_quote(OutputIterator& out) { while (!eof()) { char_type c = next_char(); if (c == sp_traits::quote) { if (!eof() && next_ == sp_traits::quote) { next_char(); *out++ = c; } else { return; } } else { *out++ = c; } } } template<class OutputIterator> bool construct_one_token(OutputIterator& out, char_type c) { if (c == sp_traits::quote) { construct_quote(out); } else if (c == sp_traits::delim) { return true; } else if (c == sp_traits::cr) { if (!eof() && next_ == sp_traits::lf) { eol_ = true; next_char(); } return true; } else if (c == sp_traits::lf) { return true; } else { *out++ = c; } return false; } struct null_out : std::iterator<std::output_iterator_tag, char_type> { null_out() : dummy() { } reference operator*() { return dummy; } null_out& operator++() { return *this; } null_out operator++(int) { return *this; } private: value_type dummy; }; public: basic_csv_reader(InputIterator first, InputIterator last) : it_(first), end_(last), eol_(false) { if (eof()) eol_ = true; else next_ = *it_; } bool eof() const { return it_ == end_; } bool eol() const { return eol_; } bool next_row() { while (!eol()) read_column(null_out()); if (!eof()) eol_ = false; return !eof(); } template<class OutputIterator> OutputIterator read_column(OutputIterator out) { if (eol()) out; while (!eof()) { char_type c = next_char(); if (construct_one_token(out, c)) { if (eof()) eol_ = true; return out; } } eol_ = true; return out; } };
// こっからテスト #include <string> #include <iostream> // 特別扱いする文字 struct special_char_traits { static const char quote = '\"'; static const char cr = '\r'; static const char lf = '\n'; static const char delim = ','; }; typedef basic_csv_reader<const char*, special_char_traits> csv_reader; int main() { // a,"b""b","c,c" // d,e,f const char input[] = "a,\"b\"\"b\",\"c,c\"\r\nd,e,f"; const char* ptr = input; csv_reader reader(input, input + sizeof(input) - 1); while (!reader.eof()) { while (!reader.eol()) { std::string str; reader.read_column(std::back_inserter(str)); std::cout << str << '|'; } std::cout << std::endl; reader.next_row(); } }
a|b"b|c,c| d|e|f|