Разбор ключа, пары значений, когда ключ не является уникальным - PullRequest
0 голосов
/ 27 ноября 2018

Мой ввод состоит из нескольких ключей, пар значений, например:

A=1, B=2, C=3, ..., A=4

Я хочу разобрать входные данные в следующий тип:

 std::map< char, std::vector< int > > m

К значениям для равных клавиш необходимо добавитьвектор.Таким образом, анализируемый вывод должен быть равен:

m['A']={1,4};
m['B']={2};
m['C']={3};

Какое простейшее решение с использованием 'boost :: spirit :: qi'?

1 Ответ

0 голосов
/ 30 ноября 2018

Вот один из способов сделать это:

#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/include/vector.hpp>
#include <boost/fusion/include/at_c.hpp>
#include <iostream>
#include <utility>
#include <string>
#include <vector>
#include <map>

namespace qi = boost::spirit::qi;
namespace fusion = boost::fusion;

int main()
{
    std::string str = "A=1, B=2, C=3, A=4";

    std::map< char, std::vector< int > > m;
    auto inserter = [&m](fusion::vector< char, int > const& parsed,
        qi::unused_type, qi::unused_type)
    {
        m[fusion::at_c< 0 >(parsed)].push_back(fusion::at_c< 1 >(parsed));
    };

    auto it = str.begin(), end = str.end();
    bool res = qi::phrase_parse(it, end,
        ((qi::char_ >> '=' >> qi::int_)[inserter]) % ',',
        qi::space);

    if (res && it == end)
        std::cout << "Parsing complete" << std::endl;
    else
        std::cout << "Parsing incomplete" << std::endl;

    for (auto const& elem : m)
    {
        std::cout << "m['" << elem.first << "'] = {";
        for (auto value : elem.second)
            std::cout << " " << value;
        std::cout << " }" << std::endl;
    }

    return 0;
}

Несколько комментариев о реализации:

  1. qi::phrase_parse являетсяАлгоритм Boost.Spirit, который принимает пару итераторов, парсер и парсер пропуска и запускает парсеры на входе, обозначенном итераторами.В процессе он обновляет начальный итератор (it в этом примере), чтобы он указывал на конец потребляемого ввода при возврате.Возвращенное значение res указывает, были ли синтаксические анализаторы успешными (то есть использованный вход мог быть успешно проанализирован).Существуют другие формы qi::phrase_parse, которые позволяют извлекать атрибуты (то есть проанализированные данные в терминах Boost.Spirit), но мы здесь не используем атрибуты, потому что у вас есть своеобразное требование к результирующей структуре контейнера.

  2. Пропускный анализатор используется для пропуска частей ввода между элементами основного синтаксического анализатора.В этом случае qi::space означает, что любые символы пробела будут игнорироваться во входных данных, так что, например, «A = 1» и «A = 1» могут быть проанализированы одинаково.Существует семейство алгоритмов qi::parse, которые не имеют анализатора пропусков и поэтому требуют, чтобы основной анализатор обрабатывал весь ввод без пропусков.

  3. Часть (qi::char_ >> '=' >> qi::int_) основного анализатора соответствует один символ , за которым следует знак равенства, а затем целое число со знаком .Знак равенства выражается в виде литерала (т. Е. Он эквивалентен синтаксическому анализатору qi::lit('=')), что означает, что он соответствует только входным данным, но не приводит к анализу данных.Следовательно, результатом этого синтаксического анализатора является атрибут, представляющий собой последовательность двух элементов - символа и целого числа.

  4. Часть синтаксического анализатора % ',' является синтаксическим анализатором списка , который анализирует любое количество частей ввода, описанных синтаксическим анализатором слева (который описан выше), разделенных фрагментами, описанными синтаксическим анализатором справа (т.е. с запятыми в нашем случае).Как и раньше, символ запятой является синтаксическим анализатором, поэтому он не производит вывод.

  5. Часть [inserter] представляет собой семантическое действие , которое являетсяфункция, которая вызывается парсером каждый раз, когда совпадает с частью входной строки.Парсер передает все свои проанализированные выходные данные в качестве первого аргумента этой функции.В нашем случае семантическое действие присоединяется к парсеру, описанному в пуле № 3, что означает, что передается последовательность символов и целое число.Boost.Spirit использует fusion::vector для передачи этих данных.Два других аргумента семантического действия в этом примере не используются и могут быть проигнорированы.

  6. Функция inserter в этом примере является лямбда-функцией, но это может быть любая другаятип объекта функции, включая обычную функцию, функцию, сгенерированную std::bind и т. д. Важной частью является то, что она имеет указанную сигнатуру и что тип ее первого аргумента совместим с атрибутом синтаксического анализатора, которому она соответствуетприкреплен как семантическое действие.Итак, если бы у нас был другой анализатор в пуле №3, этот аргумент пришлось бы изменить соответствующим образом.

  7. fusion::at_c< N >() в inserter получаетэлемент вектора по индексу N.Это очень похоже на std::get< N >() применительно к std::tuple.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...