- Published on
C++ で getline と Boost.Spirit を使って CSV ファイルを読み込む
- Authors
- Name
- Daisuke Kobayashi
- https://twitter.com
先日 C++ で整数データが約 2900 万要素ある CSV ファイルを読み込むプログラムを書く必要がありました.その時に書いたコードを貼っておきます.getline と Boost.Spirit を使った 2 種類のプログラムを試しています.固定長の配列に CSV ファイルの要素を読み込んでいます.
#include <fstream>
#include <string>
#include <boost/timer/timer.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/home/phoenix/object/construct.hpp>
#include <boost/spirit/home/phoenix/container.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
namespace phoenix = boost::phoenix;
template <typename Iterator>
bool parse_csv(Iterator first, Iterator last, std::vector<int>& vec)
{
using qi::int_;
using qi::phrase_parse;
using qi::_1;
using ascii::space;
using phoenix::push_back;
bool r = phrase_parse(first, last,
// Begin grammar
(
int_[push_back(phoenix::ref(vec), _1)] % ','
)
,
// End grammar
space);
if (first != last)
return false;
return r;
}
bool read_csv_getline(const char* filename, unsigned short* buf)
{
std::ifstream ifs(filename);
std::string line;
unsigned int index = 0;
while (std::getline(ifs, line)) {
std::stringstream ss(line);
std::string value;
while (std::getline(ss, value, ',')) {
buf[index] = atoi(value.c_str());
index++;
}
}
return true;
}
bool read_csv_spirit(const char* filename, unsigned short* buf)
{
std::ifstream ifs(filename);
std::string line;
unsigned int index = 0;
while (std::getline(ifs, line)) {
std::vector<int> read_buf;
if (parse_csv(line.begin(), line.end(), read_buf)) {
for (int i = 0; i < read_buf.size(); i++) {
buf[index] = read_buf[i];
index++;
}
} else {
return false;
}
}
return true;
}
int main() {
unsigned short* buf = new unsigned short[28829184];
boost::timer::cpu_timer t1;
read_csv_getline("data.csv", buf);
std::cout << t1.elapsed().wall / 1000000.0 << std::endl;
boost::timer::cpu_timer t2;
read_csv_spirit("data.csv", buf);
std::cout << t2.elapsed().wall / 1000000.0 << std::endl;
delete[] buf;
return 0;
}
PC にインストールしてある下記の環境で試しに処理時間を計ってみました.
- Windows 7 Professional 64 bit
- Core i7 860 2.80GHz
- 4.00 GB
Visual C++ 2012 Express + boost 1.55
std::getline: 4568.91 [msec] boost::spirit: 2734.77 [msec]
Visual C++ 2008 Professional + boost 1.54
std::getline: 7601.67 [msec] boost::spirit: 4396.55 [msec]
いずれも getline の方が spirit より 1.6-1.7 倍程度処理時間がかかるという結果になりました.