Search code examples
c++boostvectorboost-spirit

Boost Spirit syntax for recognising series of numbers from a line into vector?


I have a string that comes in such flavours:

  SCALE FACTORS      16.      0.0     0.0     0.0     0.0      .

or

  SCALE FACTORS      30.       .       .       .       .       .

or

  SCALE FACTORS     256.      10.0     20.0     30.0    .

So, several numbers with dot at the end, and dots and spaces alone. This is some historical data format, and I have too many files to adjust them manually to a more readable shape.

The start of line is always "SCALE FACTORS" that can be "read" as fixed template.

I would need a boost spirit expression that helps me to recognize this string in a vector. Dots alone must be discarded (or at least read as zeroes). Digits must be stored in vector. Number of valid digits in line is between one and several (not fixed).

My main question is, how I can store bunch of numerals in line in a vector. I can, in principle, remove stray dots without the help of Boost Spirit.


Solution

  • With a blank_type skipper, you could

        start = line % eol;
        line  = lit("SCALE") >> "FACTORS" >> *(double_|'.')
    

    Which results in zeroes for the '.' items:

    Parsed 3 lines
    [SCALE FACTORS] 16 0 0 0 0 0 
    [SCALE FACTORS] 30 0 0 0 0 0 
    [SCALE FACTORS] 256 10 20 30 0 
    Remaining input: '
    '
    

    See it Live On Coliru

    #define BOOST_SPIRIT_USE_PHOENIX_V3
    #include <boost/format.hpp>
    #include <boost/spirit/include/qi.hpp>
    #include <boost/spirit/include/phoenix.hpp>
    #include <cstdint>
    
    namespace qi  = boost::spirit::qi;
    namespace phx = boost::phoenix;
    
    typedef std::vector<double> line_t;
    typedef std::vector<line_t> lines_t;
    
    template <typename Iterator>
    struct sfparser : public qi::grammar<Iterator, lines_t(), qi::blank_type>
    {
        sfparser () : sfparser::base_type(start)
        {
            using namespace qi;
    
            start = line % eol;
            line  = lit("SCALE") >> "FACTORS" >> *(double_|'.');
        }
    
      private:
        qi::rule<Iterator, lines_t(), qi::blank_type> start;
        qi::rule<Iterator, line_t(), qi::blank_type > line;
    };
    
    int main()
    {
        std::string const input = " SCALE FACTORS      16.      0.0     0.0     0.0     0.0      .\n"
            "SCALE FACTORS      30.       .       .       .       .       .\n"
            "  SCALE FACTORS     256.      10.0     20.0     30.0    .\n";
    
        std::string::const_iterator f = input.begin();
        std::string::const_iterator l = input.end();
        sfparser<std::string::const_iterator> grammar;
    
        lines_t data;
        if (qi::phrase_parse (f, l, grammar, qi::blank, data))
        {
            std::cout << "Parsed " << data.size() << " lines\n";
            for (auto& line : data)
            {
                std::cout << "[SCALE FACTORS] ";
                for (auto d : line)
                    std::cout << d << " ";
    
                std::cout << "\n";
            }
        }
        else
        {
            std::cout << "Failed\n";
        }
    
        if (f!=l)
            std::cout << "Remaining input: '" << std::string(f,l) << "'\n";
    }