Повысьте проблемы со шкипером
У меня проблемы с бодибилдерами.
Мне нужно разобрать файл так:
ROW int
int [int, int]
int [int, int]
...
Я могу разобрать его без проблем (благодаря stackru;), только если я добавлю '_' после первого int.
На самом деле, я думаю, что шкипер съедает конец строки после первого int, поэтому первая и вторая (на второй строке) выглядят как только одно int. Я не понимаю, как сохранить eol, но есть места. Я нашел примеры использования пользовательского парсера, как здесь и здесь.
Я пробовал qi:: blank, пользовательский парсер с одним освещенным правилом (''). Независимо от того, какой шкипер я использую, пробел и eol всегда есть.
Моя грамматика:
строка:
struct rowType
{
unsigned int number;
std::list<unsigned int> list;
};
полная проблема хранится в структуре:
struct problemType
{
unsigned int ROW;
std::vector<rowType> rows;
};
анализатор строк:
template<typename Iterator>
struct row_parser : qi::grammar<Iterator, rowType(), qi::space_type>
{
row_parser() : row_parser::base_type(start)
{
list = '[' >> -(qi::int_ % ',') >> ']';
start = qi::int_ >> list;
}
qi::rule<Iterator, rowType(), qi::space_type> start;
qi::rule<Iterator, std::list<unsigned int>(), qi::space_type> list;
};
и анализатор проблем:
template<typename Iterator>
struct problem_parser : qi::grammar<Iterator,problemType(),qi::space_type>
{
problem_parser() : problem_parser::base_type(start)
{
using boost::phoenix::bind;
using qi::lit;
start = qi::int_ >> lit('_') >> +(row);
//BOOST_SPIRIT_DEBUG_NODE(start);
}
qi::rule<Iterator, problemType(),qi::space_type> start;
row_parser<Iterator> row;
};
И я использую это так:
main() {
static const problem_parser<spirit::multi_pass<base_iterator_type> > p;
...
spirit::qi::phrase_parse(first, last ,
p,
qi::space,
pb);
}
Конечно, qi:: space - это моя проблема, и один из способов решить эту проблему - не использовать шкипер, но для81 это нужно для фразы, а для моего парсера - один.
Я застрял уже несколько часов... Я думаю, что-то очевидно, что я неправильно понял.
Спасибо за вашу помощь.
1 ответ
В целом, следующие директивы полезны для запрета / переключения шкипера в середине грамматики:
qi::lexeme [ p ]
что запрещает шкипер, например, если вы хотите быть уверены, что вы анализируете идентификатор без внутренних пропусков)qi::raw [ p ]
который анализирует как всегда, включая пропуски, но возвращает необработанный диапазон итераторов для соответствующей исходной последовательности (включая пропущенные позиции)qi::no_skip [ p ]
Блокирование пропуска без предварительного пропускаqi::skip(s) [ p ]
который заменяет шкипера другим шкиперомs
в целом (обратите внимание, что вам нужно использовать соответствующим образом объявленныеqi::rule<>
случаи внутри такогоskip[]
оговорка)
где p
это любое выражение парсера.
Конкретное решение
Ваша проблема, как вы уже знаете, может быть qi::space
съедает все пробелы. Я не могу знать, что не так в вашей грамматике (поскольку вы не показываете ни полную грамматику, ни соответствующий ввод).
Поэтому вот что я бы написал. Заметка
- использование
qi::eol
явно требовать разрывов строк в определенных местах - использование
qi::blank
как шкипер (не включаяeol
) - для краткости я объединил грамматику
Код:
#define BOOST_SPIRIT_DEBUG
#include <boost/fusion/adapted.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
namespace qi = boost::spirit::qi;
namespace phx = boost::phoenix;
struct rowType {
unsigned int number;
std::list<unsigned int> list;
};
struct problemType {
unsigned int ROW;
std::vector<rowType> rows;
};
BOOST_FUSION_ADAPT_STRUCT(rowType, (unsigned int, number)(std::list<unsigned int>, list))
BOOST_FUSION_ADAPT_STRUCT(problemType, (unsigned int, ROW)(std::vector<rowType>, rows))
template<typename Iterator>
struct problem_parser : qi::grammar<Iterator,problemType(),qi::blank_type>
{
problem_parser() : problem_parser::base_type(problem)
{
using namespace qi;
list = '[' >> -(int_ % ',') >> ']';
row = int_ >> list >> eol;
problem = "ROW" >> int_ >> eol >> +row;
BOOST_SPIRIT_DEBUG_NODES((problem)(row)(list));
}
qi::rule<Iterator, problemType() , qi::blank_type> problem;
qi::rule<Iterator, rowType() , qi::blank_type> row;
qi::rule<Iterator, std::list<unsigned int>(), qi::blank_type> list;
};
int main()
{
const std::string input =
"ROW 1\n"
"2 [3, 4]\n"
"5 [6, 7]\n";
auto f = begin(input), l = end(input);
problem_parser<std::string::const_iterator> p;
problemType data;
bool ok = qi::phrase_parse(f, l, p, qi::blank, data);
if (ok) std::cout << "success\n";
else std::cout << "failed\n";
if (f!=l)
std::cout << "Remaining unparsed: '" << std::string(f,l) << "'\n";
}
Если вы действительно не хотите требовать разрывов строк:
template<typename Iterator>
struct problem_parser : qi::grammar<Iterator,problemType(),qi::space_type>
{
problem_parser() : problem_parser::base_type(problem)
{
using namespace qi;
list = '[' >> -(int_ % ',') >> ']';
row = int_ >> list;
problem = "ROW" >> int_ >> +row;
BOOST_SPIRIT_DEBUG_NODES((problem)(row)(list));
}
qi::rule<Iterator, problemType() , qi::space_type> problem;
qi::rule<Iterator, rowType() , qi::space_type> row;
qi::rule<Iterator, std::list<unsigned int>(), qi::space_type> list;
};
int main()
{
const std::string input =
"ROW 1 " // NOTE whitespace, obviously required!
"2 [3, 4]"
"5 [6, 7]";
auto f = begin(input), l = end(input);
problem_parser<std::string::const_iterator> p;
problemType data;
bool ok = qi::phrase_parse(f, l, p, qi::space, data);
if (ok) std::cout << "success\n";
else std::cout << "failed\n";
if (f!=l)
std::cout << "Remaining unparsed: '" << std::string(f,l) << "'\n";
}
Обновить
В ответ на комментарий: вот фрагмент, который показывает, как читать входные данные из файла. Это было проверено и отлично работает для меня:
std::ifstream ifs("input.txt"/*, std::ios::binary*/);
ifs.unsetf(std::ios::skipws);
boost::spirit::istream_iterator f(ifs), l;
problem_parser<boost::spirit::istream_iterator> p;