1

I would like to read a simple 2D int array (whitespace separated) as here:

qi::phrase_parse(b, e, +qi::int_ % qi::eol, qi::space - qi::eol, vectors)

There are two differences, though:

  1. I want to put it into a 1D std::vector, line by line, without separating
  2. If two lines have a different amount of ints, this shall be recognized as a parsing error.

Is it possible to do this as a one liner, e.g. without writing my own parser? Just as simple as in the link mentioned above?

Community
  • 1
  • 1
Johannes
  • 2,901
  • 5
  • 30
  • 50
  • Sepcifically _which_ example in the mnentioned link do you mean? I posted widely varying approaches there, all of which could very easily be adapted to your problem. Do tell us where you are stuck (perhaps [show us whay you have got](http://stackoverflow.com/questions/how-to-ask)) – sehe Apr 21 '13 at 11:57
  • @sehe I updated it and pasted the line of code. Actually, I could write my own parser for that, but I need many of those and was hoping I could do it as a one liner. – Johannes Apr 21 '13 at 15:28
  • I just posted an answer assuming that in deed this was the 'oneliner' you might have meant. Hope it helps (especially the linked answer, since it could help you factor out repeated code). – sehe Apr 21 '13 at 15:31
  • @sehe Thanks. But actually, I looked for storing directly into a 1D std::vector, and the error check performed while parsing (as a parsing error). – Johannes Apr 21 '13 at 16:00
  • How would you know how many "elements per row" to expect/to interpret the data as? – sehe Apr 21 '13 at 16:32
  • @sehe I would know after reading the first line – Johannes Apr 21 '13 at 17:14
  • Updated my answer, the second example detects elements-per-row from the first row. Aaand it collects into a 1D vector (Note this required 0 changes to the parser, as Qi will just keep appending to the same vector if only 1 was given. Magic!). – sehe Apr 21 '13 at 17:47

1 Answers1

3

Assuming you meant the Spirit version ("as a one liner"), below is an adapted version that adds the check for number of elements.

Should you want more control (and on the fly input checking, instead of 'in hindsight') then I recommend you look at another answer I wrote that shows three approaches to do this:

.

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/support_istream_iterator.hpp>
#include <boost/spirit/include/karma.hpp>

namespace spirit = boost::spirit;
namespace qi     = boost::spirit::qi;
namespace karma  = boost::spirit::karma;

int main()
{
    std::cin.unsetf(std::ios::skipws);
    spirit::istream_iterator b(std::cin), e;

    std::vector<std::vector<int> > vectors;

    if (qi::phrase_parse(b, e, +qi::int_ % qi::eol >> qi::eoi, qi::blank, vectors))
    {
        std::cerr << "Parse failed at '" << std::string(b,e) << "'\n";
        return 255;
    }

    // check all rows have equal amount of elements:
    const auto number_of_elements = vectors.front().size();
    for (auto& v : vectors)
        if (v.size() != number_of_elements)
            std::cerr << "Unexpected number of elements: " << v.size() << " (expected: " << number_of_elements << ")\n";

    // print the data for verification
    std::cout 
        << karma::format(karma::right_align(8)[karma::auto_] % ',' % '\n', vectors)
        << std::endl;

    return 0;
}

The karma bits are not necessary (they're just there to output the whole thing for demonstration).

UPDATE

To build in more active error checking, you could do:

int num_elements = 0;
bool ok = qi::phrase_parse(b, e, 
        (+qi::int_) [ phx::ref(num_elements) = phx::size(qi::_1) ]
     >> *(qi::eol >> qi::repeat(phx::ref(num_elements)) [ qi::int_ ])
     >> *qi::eol, 
     qi::blank, vectors);

Which uses qi::repeat to expect the num_elements number of elements on subsequent lines. You can just store that into a 1-dimensional array:

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/support_istream_iterator.hpp>
#include <boost/spirit/include/karma.hpp>

namespace qi     = boost::spirit::qi;
namespace phx    = boost::phoenix;
namespace karma  = boost::spirit::karma;

int main()
{
    std::cin.unsetf(std::ios::skipws);
    boost::spirit::istream_iterator b(std::cin), e;

    //std::vector<std::vector<int> > vectors;
    std::vector<int> vectors;

    int num_elements = 0;
    bool ok = qi::phrase_parse(b, e, 
            (+qi::int_) [ phx::ref(num_elements) = phx::size(qi::_1) ]
         >> *(qi::eol >> qi::repeat(phx::ref(num_elements)) [ qi::int_ ])
         >> *qi::eol, 
         qi::blank, vectors);

    std::cout << "Detected num_elements: " << num_elements << "\n";

    if (!ok)
    {
        std::cerr << "Parse failed at '" << std::string(b,e) << "'\n";
        return 255;
    }

    if (b!=e)
        std::cout << "Trailing unparsed: '" << std::string(b,e) << "'\n";

    // print the data for verification
    std::cout 
        << karma::format_delimited(karma::columns(num_elements)[+karma::int_], ' ', vectors)
        << std::endl;

    return 0;
}

Note the use of karma::columns(num_elements) to split the output into the correct number of columns per row.

Community
  • 1
  • 1
sehe
  • 374,641
  • 47
  • 450
  • 633
  • @Johannes Cheers :) I reckon, if you format it accordingly, it could (technically) still be considered a oneliner :) [I personally love the showcase of Karma to format it with N columns, actually] – sehe Apr 22 '13 at 06:55