【问题标题】:C++ boost::qi parse space and newline delimited numbers as a 2D vectorC++ boost::qi 将空格和换行符分隔的数字解析为二维向量
【发布时间】:2020-11-16 10:17:33
【问题描述】:

我有多个浮点数行,一行中的数字用空格分隔 例如

1.2 2.2 3.2
1.1 2.1 3.1

我想将上述数字提取为字符串并解析为二维向量; std::vector< std::vector< std::string > > { {"1.2", "2.2", "3.2"},{"1.1", "2.1", "3.1} }

我的代码如下。

#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/io.hpp>
#include <string>

namespace client
{
    namespace qi = boost::spirit::qi;
    namespace ascii = boost::spirit::ascii;

    using VecType = std::vector< std::vector< std::string >>; 
    
    struct employee
    {
        VecType name;
    };
}

BOOST_FUSION_ADAPT_STRUCT(
    client::employee,
    (client::VecType, name)
)
//]

namespace client
{
    template <typename Iterator>
    struct employee_parser : qi::grammar<Iterator, VecType(), ascii::space_type>
    {
        employee_parser() : employee_parser::base_type(start)
        {
            using qi::lexeme;
            using ascii::char_;

            number %= lexeme[+char_( "0-9." ) >>  qi::space ];
            start %= +number;
        }

        qi::rule<Iterator, std::string(), ascii::space_type> number;
        qi::rule<Iterator, VecType(), ascii::space_type> start;
    };
}

但这会产生二维向量,外部向量大小为 6,每个内部向量大小为 1。

我不明白如何将字符串从新行中拆分为仅生成 2 个内部向量。

【问题讨论】:

    标签: c++ boost newline space qi


    【解决方案1】:

    你必须拆分规则。让我们从类型开始:

    using VecType = std::vector<std::string>;
    using VecVecType = std::vector<VecType>;
    

    现在,让我们制定一个规则来解析一个数字、一行数字和多行:

    qi::rule<Iterator, std::string()> number;
    qi::rule<Iterator, VecType(), qi::blank_type> row;
    qi::rule<Iterator, VecVecType()> start;
    

    实现它们(请注意,我将船长移到了语法中,因为将其泄漏到界面中并不是一个好主意):

    number = raw [ double_ ]; // raw[] to get string value
    row    = +number;
    start  = qi::skip(blank) [ row % eol ];
    

    注意:我使用了blank 而不是space,因为我们不想跳过对语法很重要的eol

    演示

    Live On Coliru

    #include <boost/spirit/include/qi.hpp>
    #include <string>
    #include <iomanip>
    
    namespace client {
        namespace qi = boost::spirit::qi;
    
        using VecType = std::vector<std::string>;
        using VecVecType = std::vector<VecType>;
    } // namespace client
    
    namespace client {
        template <typename Iterator>
        struct my_parser : qi::grammar<Iterator, VecVecType()> {
            my_parser() : my_parser::base_type(start) {
                using namespace qi;
    
                number = raw [ double_ ]; // raw[] to get string value
                row    = *number;
                start  = qi::skip(blank) [ row % eol ];
            }
    
            qi::rule<Iterator, std::string()> number;
            qi::rule<Iterator, VecType(), qi::blank_type> row;
            qi::rule<Iterator, VecVecType()> start;
        };
    } // namespace client
    
    int main() {
        client::my_parser<std::string::const_iterator> const p;
        for (std::string const& input: {
                "",
                "1.2 2.2 3.2\n1.1 2.1 3.1",
                })
        {
            std::cout << "--- " << std::quoted(input) << " -----\n";
            auto f = begin(input), l = end(input);
            client::VecVecType output;
            if (parse(f, l, p, output)) {
                std::cout << "Parsed:\n";
                for (auto& row : output) {
                    for (auto& v : row) {
                        std::cout << "\t" << v;
                    }
                    std::cout << "\n";
                }
            } else {
                std::cout << "Failed\n";
            }
            if (f!=l) {
                std::cout << "Remaining input: " << std::quoted(std::string(f,l)) << "\n";
            }
        }
    }
    

    打印

    --- "" -----
    Parsed:
    
    --- "1.2 2.2 3.2
    1.1 2.1 3.1" -----
    Parsed:
        1.2 2.2 3.2
        1.1 2.1 3.1
    

    奖金

    强类型化让一切变得更有趣:如果可以解析成双精度,为什么还要解析成字符串?

    还展示了如何启用规则调试:

    Live On Coliru

    #define BOOST_SPIRIT_DEBUG
    #include <boost/spirit/include/qi.hpp>
    #include <string>
    #include <iomanip>
    
    namespace client {
        namespace qi = boost::spirit::qi;
    
        using VecType = std::vector<double>;
        using VecVecType = std::vector<VecType>;
    } // namespace client
    
    namespace client {
        template <typename Iterator>
        struct my_parser : qi::grammar<Iterator, VecVecType()> {
            my_parser() : my_parser::base_type(start) {
                using namespace qi;
    
                row    = *double_;
                start  = qi::skip(blank) [ row % eol ];
    
                BOOST_SPIRIT_DEBUG_NODES((start)(row))
            }
    
          private:
            qi::rule<Iterator, VecType(), qi::blank_type> row;
            qi::rule<Iterator, VecVecType()> start;
        };
    } // namespace client
    
    int main() {
        client::my_parser<std::string::const_iterator> const p;
        for (std::string const& input: {
                "",
                "1.2 2.2 3.2\n1.1 2.1 3.1",
                })
        {
            std::cout << "--- " << std::quoted(input) << " -----\n";
            auto f = begin(input), l = end(input);
            client::VecVecType output;
            if (parse(f, l, p, output)) {
                std::cout << "Parsed:\n";
                for (auto& row : output) {
                    for (auto& v : row) {
                        std::cout << "\t" << v;
                    }
                    std::cout << "\n";
                }
            } else {
                std::cout << "Failed\n";
            }
            if (f!=l) {
                std::cout << "Remaining input: " << std::quoted(std::string(f,l)) << "\n";
            }
        }
    }
    

    打印

    --- "" -----
    <start>
      <try></try>
      <row>
        <try></try>
        <success></success>
        <attributes>[[]]</attributes>
      </row>
      <success></success>
      <attributes>[[[]]]</attributes>
    </start>
    Parsed:
    
    --- "1.2 2.2 3.2
    1.1 2.1 3.1" -----
    <start>
      <try>1.2 2.2 3.2\n1.1 2.1 </try>
      <row>
        <try>1.2 2.2 3.2\n1.1 2.1 </try>
        <success>\n1.1 2.1 3.1</success>
        <attributes>[[1.2, 2.2, 3.2]]</attributes>
      </row>
      <row>
        <try>1.1 2.1 3.1</try>
        <success></success>
        <attributes>[[1.1, 2.1, 3.1]]</attributes>
      </row>
      <success></success>
      <attributes>[[[1.2, 2.2, 3.2], [1.1, 2.1, 3.1]]]</attributes>
    </start>
    Parsed:
        1.2 2.2 3.2
        1.1 2.1 3.1
    

    【讨论】:

    • 节省 CPU 和树:x3 + libfmt in 2020
    • 其实数据可以是任何类型的,所以我只想先把它们读入字符串,然后再处理类型
    • 感谢@sehe 的回答。
    猜你喜欢
    • 1970-01-01
    • 2021-06-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-18
    相关资源
    最近更新 更多