c++ - 在 C++ 中标记 "Braced Initializer List"样式的字符串(使用 Boost?)

标签 c++ boost c++17 boost-spirit boost-tokenizer

我有一个字符串(甚至是嵌套字符串),其格式类似于 C++ 大括号初始值设定项列表。我想将它们一次一层地标记为字​​符串 vector 。

因此,当我向函数输入“{一,二,三}”时,应该输出一个三元素 vector

“一”

“两个”

“三”

为了使这一点变得复杂,它需要支持带引号的标记并保留嵌套列表:

输入字符串:"{一,{2,\"三四\"}},\"五,六\",{\"七,八\"}}"

输出是一个四元素 vector :

“一”

"{2,\"三四\"}",

“五,六”

"{\"七、八\"}"

我看过其他一些 SO 帖子:

Using Boost Tokenizer escaped_list_separator with different parameters

Boost split not traversing inside of parenthesis or braces

并使用它们来启动解决方案,但这对于分词器来说似乎有点太复杂了(因为大括号):

#include <boost/algorithm/string.hpp>
#include <boost/tokenizer.hpp>

std::vector<std::string> TokenizeBracedList(const std::string& x)
{
  std::vector<std::string> tokens;

  std::string separator1("");
  std::string separator2(",\n\t\r");
  std::string separator3("\"\'");

  boost::escaped_list_separator<char> elements(separator1, separator2, separator3);
  boost::tokenizer<boost::escaped_list_separator<char>> tokenizer(x, elements);

  for(auto i = std::begin(tokenizer); i != std::end(tokenizer); ++i)
  {
    auto token = *i;
    boost::algorithm::trim(token);
    tokens.push_back(token);
  }

  return tokens;
}

有了这个,即使在微不足道的情况下,它也不会删除左大括号和右大括号。

Boost 和 C++17 是一个公平的解决方案。

最佳答案

简单(平坦)拍摄

定义一个平面数据结构,例如:

using token  = std::string;
using tokens = std::vector<token>;

我们可以定义一个 X3 解析器,如下所示:

namespace Parser {
    using namespace boost::spirit::x3;

    rule<struct list_, token> item;

    auto quoted   = lexeme [ '"' >> *('\\' >> char_ | ~char_('"')) >> '"' ];
    auto bare     = lexeme [ +(graph-','-'}') ];

    auto list     = '{' >> (item % ',') >> '}';
    auto sublist  = raw [ list ];

    auto item_def = sublist | quoted | bare;

    BOOST_SPIRIT_DEFINE(item)
}

Live On Wandbox

#include <boost/spirit/home/x3.hpp>
#include <iostream>
#include <iomanip>

using token  = std::string;
using tokens = std::vector<token>;

namespace x3 = boost::spirit::x3;

namespace Parser {
    using namespace boost::spirit::x3;

    rule<struct list_, token> item;

    auto quoted   = lexeme [ '"' >> *('\\' >> char_ | ~char_('"')) >> '"' ];
    auto bare     = lexeme [ +(graph-','-'}') ];

    auto list     = '{' >> (item % ',') >> '}';
    auto sublist  = raw [ list ];

    auto item_def = sublist | quoted | bare;

    BOOST_SPIRIT_DEFINE(item)
}

int main() {
    for (std::string const input : {
            R"({one, "five, six"})",
            R"({one, {2, "three four"}, "five, six", {"seven, eight"}})",
        })
    {
        auto f = input.begin(), l = input.end();

        std::vector<std::string> parsed;
        bool ok = phrase_parse(f, l, Parser::list, x3::space, parsed);

        if (ok) {
            std::cout << "Parsed: " << parsed.size() << " elements\n";
            for (auto& el : parsed) {
                std::cout << " - " << std::quoted(el, '\'') << "\n";
            }
        } else {
            std::cout << "Parse failed\n";
        }

        if (f != l)
            std::cout << "Remaining unparsed: " << std::quoted(std::string{f, l}) << "\n";
    }
}

打印

Parsed: 2 elements
 - 'one'
 - 'five, six'
Parsed: 4 elements
 - 'one'
 - '{2, "three four"}'
 - 'five, six'
 - '{"seven, eight"}'

嵌套数据

将数据结构更改为更加具体/现实:

namespace ast {
    using value = boost::make_recursive_variant<
            double,
            std::string,
            std::vector<boost::recursive_variant_>
        >::type;
    using list = std::vector<value>;
}

现在我们可以更改语法,因为我们不再需要将 sublist 视为字符串:

namespace Parser {
    using namespace boost::spirit::x3;

    rule<struct item_, ast::value> item;

    auto quoted   = lexeme [ '"' >> *('\\' >> char_ | ~char_('"')) >> '"' ];
    auto bare     = lexeme [ +(graph-','-'}') ];

    auto list     = x3::rule<struct list_, ast::list> {"list" }
                  = '{' >> (item % ',') >> '}';

    auto item_def = list | double_ | quoted | bare;

    BOOST_SPIRIT_DEFINE(item)
}

一切“仍然有效”: Live On Wandbox

#include <boost/spirit/home/x3.hpp>
#include <iostream>
#include <iomanip>

namespace ast {
    using value = boost::make_recursive_variant<
            double,
            std::string,
            std::vector<boost::recursive_variant_>
        >::type;
    using list = std::vector<value>;
}

namespace x3 = boost::spirit::x3;

namespace Parser {
    using namespace boost::spirit::x3;

    rule<struct item_, ast::value> item;

    auto quoted   = lexeme [ '"' >> *('\\' >> char_ | ~char_('"')) >> '"' ];
    auto bare     = lexeme [ +(graph-','-'}') ];

    auto list     = x3::rule<struct list_, ast::list> {"list" }
                  = '{' >> (item % ',') >> '}';

    auto item_def = list | double_ | quoted | bare;

    BOOST_SPIRIT_DEFINE(item)
}

struct pretty_printer {
    using result_type = void;
    std::ostream& _os;
    int _indent;

    pretty_printer(std::ostream& os, int indent = 0) : _os(os), _indent(indent) {}

    void operator()(ast::value const& v) { boost::apply_visitor(*this, v); }

    void operator()(double v)            { _os << v; }
    void operator()(std::string s)       { _os << std::quoted(s); }
    void operator()(ast::list const& l)  {
        _os << "{\n";
        _indent += 2;
        for (auto& item : l) {
            _os << std::setw(_indent) << "";
            operator()(item);
           _os << ",\n";
        }
        _indent -= 2;
        _os << std::setw(_indent) << "" << "}";
    }
};

int main() {
    pretty_printer print{std::cout};

    for (std::string const input : {
            R"({one, "five, six"})",
            R"({one, {2, "three four"}, "five, six", {"seven, eight"}})",
        })
    {
        auto f = input.begin(), l = input.end();

        ast::value parsed;
        bool ok = phrase_parse(f, l, Parser::item, x3::space, parsed);

        if (ok) {
            std::cout << "Parsed: ";
            print(parsed);
            std::cout << "\n";
        } else {
            std::cout << "Parse failed\n";
        }

        if (f != l)
            std::cout << "Remaining unparsed: " << std::quoted(std::string{f, l}) << "\n";
    }
}

打印:

Parsed: {
  "one",
  "five, six",
}
Parsed: {
  "one",
  {
    2,
    "three four",
  },
  "five, six",
  {
    "seven, eight",
  },
}

关于c++ - 在 C++ 中标记 "Braced Initializer List"样式的字符串(使用 Boost?),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49723718/

相关文章:

C++ FLTK 静止组件

模板类中模板函数的 C++ 特化

c++ - Boost 将派生类反序列化为基类指针

c++ - future 的类型有可能转变吗?

c++ - 在 C++ 中动态声明数据类型

c++ - 使用 boost asio 接收文本的最有效方式?

c++ - 如何配置为非阻塞unique_lock?

c++ - 使用折叠表达式合并多个 vector

gcc - 如何抑制 GCC 编译器警告 : inline variables are only available with -std=c++1z or -std=gnu++1z

c++ - 使用单成员结构作为成员本身