Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/amazon-web-services/14.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 用于解析标题列的boost-spirit语法_C++_Boost Spirit Qi - Fatal编程技术网

C++ 用于解析标题列的boost-spirit语法

C++ 用于解析标题列的boost-spirit语法,c++,boost-spirit-qi,C++,Boost Spirit Qi,我想解析文本文件的标题列。应允许引用列名和任何字母。目前我使用的语法如下: #include <string> #include <iostream> #include <boost/spirit/include/qi.hpp> namespace qi = boost::spirit::qi; template <typename Iterator, typename Skipper> struct Grammar : qi::grammar

我想解析文本文件的标题列。应允许引用列名和任何字母。目前我使用的语法如下:

#include <string>
#include <iostream>
#include <boost/spirit/include/qi.hpp>

namespace qi = boost::spirit::qi;

template <typename Iterator, typename Skipper>
struct Grammar : qi::grammar<Iterator, void(), Skipper>
{
        static constexpr char colsep = '|';
        Grammar() : Grammar::base_type(header)
        {
                using namespace qi;
                using ascii::char_;
#define COL(name) (no_case[name] | ('"' >> no_case[name] >> '"'))
                header = (COL("columna") | COL("column_a")) >> colsep >>
                        (COL("columnb") | COL("column_b")) >> colsep >>
                        (COL("columnc") | COL("column_c")) >> eol >> eoi;
#undef COL
        }
        qi::rule<Iterator, void(), Skipper> header;
};

int main()
{
        const std::string s{"columnA|column_B|column_c\n"};
        auto begin(std::begin(s)), end(std::end(s));
        Grammar<std::string::const_iterator, qi::blank_type> p;
        bool ok = qi::phrase_parse(begin, end, p, qi::blank);

        if (ok && begin == end)
                std::cout << "Header ok" << std::endl;
        else if (ok && begin != end)
                std::cout << "Remaining unparsed: '" << std::string(begin, end) << "'" << std::endl;
        else
                std::cout << "Parse failed" << std::endl;
        return 0;
}
col是一个合适的语法或规则

@sehe如何修复此语法以同时支持
“Column\u A\”

这时你可能已经意识到这里发生了两件不同的事情

单独关注 一方面,您有一个语法(允许
|
分隔列,如
columna
列a

另一方面,您有语义分析(检查解析内容是否符合某些标准的阶段)

让你的生活变得艰难的是试图将两者混为一谈。现在,不要误会我的意思,可能有(非常罕见的)情况下,将这些责任融合在一起是绝对必要的——但我觉得这将永远是一种优化。如果您需要,Spirit不是您的东西,您更可能得到手写解析器的服务

解析 所以,让我们简单了解一下语法:

static auto headers = (quoted|bare) % '|' > (eol|eoi);
bare
quoted
规则可以与以前几乎相同:

static auto quoted  = lexeme['"' >> *('\\' >> char_ | "\"\"" >> attr('"') | ~char_('"')) >> '"'];
static auto bare    = *(graph - '|');
正如您所看到的,这将隐式地处理引用和转义以及在词素之外跳过空白。简单应用时,将生成一个列名称的干净列表:

std::string const s = "\"columnA\"|column_B| column_c \n";

std::vector<std::string> headers;
bool ok = phrase_parse(begin(s), end(s), Grammar::headers, x3::blank, headers);

std::cout << "Parse " << (ok?"ok":"invalid") << std::endl;
if (ok) for(auto& col : headers) {
    std::cout << std::quoted(col) << "\n";
}
间奏曲:编码风格 让我们构造代码,以便反映关注点的分离。我们的解析代码可能使用X3,但我们的验证代码不需要位于同一个翻译单元(cpp文件)中

具有定义一些基本类型的标题:

#include <string>
#include <vector>

using Header = std::string;
using Headers = std::vector<Header>;
现在,
main
可以重写为:

auto headers = parse_headers("\"columnA\"|column_B| column_c \n");

for(auto& col : headers) {
    std::cout << std::quoted(col) << "\n";
}

bool valid = headers_match(headers, {"columna","columnb","columnc"});
std::cout << "Validation " << (valid?"passed":"failed") << "\n";
验证 这就是所谓的“语义检查”。取字符串向量,并根据逻辑进行检查:

#include <boost/range/adaptors.hpp>
#include <boost/algorithm/string.hpp>

bool header_match(Header const& actual, Header const& expected) {
    using namespace boost::adaptors;
    auto significant = [](unsigned char ch) {
        return ch != '_' && std::isgraph(ch);
    };

    return boost::algorithm::iequals(actual | filtered(significant), expected);
}

bool headers_match(Headers const& actual, Headers const& expected) {
    return boost::equal(actual, expected, header_match);
}
#包括
#包括
bool头匹配(头常数和实际值,头常数和预期值){
使用名称空间boost::适配器;
自动有效=[](无符号字符ch){
返回ch!='''&&std::isgraph(ch);
};
返回boost::算法::iequals(实际|过滤(有效),预期);
}
bool头匹配(头常量和实际值,头常量和预期值){
返回boost::相等(实际、预期、标题匹配);
}
就这些。所有的算法和现代C++的能力都可以处理,不需要因为解析上下文而约束。

完整演示 以上内容,

Parse ok
"columnA"
"column_B"
"column_c"
这两部分都变得非常简单:

  • 您的解析器不必处理奇怪的比较逻辑
  • 您的比较逻辑不必处理语法问题(引号、转义符、分隔符和空格)

任何情况下都不要求用小写字母表示文字。谢谢,我修复了这个示例。我很确定这不是你所希望的,但我想我会这么做的。在功能上,组成X3语法似乎在
词素[no\u case[]]
中跳过
'
。我不确定我是否会勉强通过。相反,你可以为标题创建一个自定义解析器。至少黑客方法可以直接转换成Qi:这是一个很好的技巧,可以剥离所有内容并比较完整的标题字符串。谢谢。我倾向于忘记那些仍然喜欢气分析器的人。好消息是,所有更改现在都包含在
parser\u headers.cpp
文件中:(或)
auto headers = parse_headers("\"columnA\"|column_B| column_c \n");

for(auto& col : headers) {
    std::cout << std::quoted(col) << "\n";
}

bool valid = headers_match(headers, {"columna","columnb","columnc"});
std::cout << "Validation " << (valid?"passed":"failed") << "\n";
#include <boost/spirit/home/x3.hpp>

namespace x3 = boost::spirit::x3;

namespace Grammar {
    using namespace x3;
    static auto quoted  = lexeme['"' >> *('\\' >> char_ | "\"\"" >> attr('"') | ~char_('"')) >> '"'];
    static auto bare    = *(graph - '|');
    static auto headers = (quoted|bare) % '|' > (eol|eoi);
}

Headers parse_headers(std::string const& input) {
    Headers output;
    if (phrase_parse(begin(input), end(input), Grammar::headers, x3::blank, output))
        return output;
    return {}; // or throw, if you prefer
}
#include <boost/range/adaptors.hpp>
#include <boost/algorithm/string.hpp>

bool header_match(Header const& actual, Header const& expected) {
    using namespace boost::adaptors;
    auto significant = [](unsigned char ch) {
        return ch != '_' && std::isgraph(ch);
    };

    return boost::algorithm::iequals(actual | filtered(significant), expected);
}

bool headers_match(Headers const& actual, Headers const& expected) {
    return boost::equal(actual, expected, header_match);
}