Read 2D array in C++ using boost::spirit

醉酒当歌 提交于 2019-12-24 13:00:53

问题


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?


回答1:


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:

  • Boost::Spirit::QI parser: index of parsed element

.

#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.



来源:https://stackoverflow.com/questions/16119548/read-2d-array-in-c-using-boostspirit

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!