Search code examples
c++boostboost-spirit-qi

boost qi::phrase_parse reads only first element


I've implemented simple ascii parser using boost::spirit. target ascii file looks like

n

0 23 45 10.0 0.5

.....

n-1 x y .....

but it returns in measure_list only 1 element

if I am trying to read ASCII as a simple vector<double> instead of structured for example - it works fine. Whats wrong?

struct measure
{
   int id;
   double x, y, size_, angle;
} 

BOOST_FUSION_ADAPT_STRUCT(measure, (int, id)(double, x)(double, y)(double, size_)(double, angel))

typedef std::vector<measure> data_t;

void RelativeMeasure(string filename)
        {
                clear();

                if(!filesystem::exists(filename)) return;

                file_name = filename;



                ifstream calibration_file(filename);

                if(calibration_file.is_open())
                {
                        int key_count;
                        calibration_file >> key_count;

                        istreambuf_iterator<char> eos;
                        istreambuf_iterator<char> it(calibration_file);

                        std::string strver(it, eos);

                        std::vector<measure> measure_list;
                        measure_list.reserve(100000);

                        qi::phrase_parse(strver.begin(), strver.end(), (qi::int_ > qi::double_ > qi::double_ > qi::double_ > qi::double_) % qi::eol, qi::blank, measure_list);

                        for each(auto measure in measure_list) key_list.push_back(KeyPoint(measure.x, measure.y, measure.size_, measure.angel));
}

Solution

  • The most likely culprit that I see is that you don't eat the newline after n. Perhaps also use +qi::eol as the delimiter.

    But that doesn't explain that you'd read the first entry.

    You could simplify things by using the streaming API (using boost::spirit::istream_iterator multi-pass adaptor under the hood):

    Live On Coliru

    void RelativeMeasure(std::string filename)
    {
        std::ifstream calfile(filename, std::ios::binary);
    
        int key_count;
        std::vector<measure> measure_list;
    
        using namespace qi;
        if (
            calfile >> std::noskipws >> phrase_match(int_, blank, key_count)
            && calfile >> phrase_match(qi::repeat(key_count)[int_ > double_ > double_ > double_ > double_ > +eol], blank, measure_list)
        )
        {
            std::vector<KeyPoint> key_list;
            key_list.reserve(measure_list.size());
            // using a converting constructor (why not parse into this at once?)
            std::copy(measure_list.begin(), measure_list.end(), back_inserter(key_list));
        }
    }