我如何获取解析的输出并使用它来查找符号
How do I take the output of a parse and use it to look up in a symbols
从代码中可以看出,我获取了一次解析的输出,并在第二次解析中使用它从符号中查找数字。
作为一条规则,我该如何做到这一点?查看文档并进行大量搜索让我相信这可以通过本地变量来完成,但我无法弄清楚如何在该变量上使用我的符号四边形。
int main()
{
using boost::phoenix::ref;
using qi::_1;
using qi::_val;
using qi::no_case;
using qi::_a;
using qi::symbols;
using qi::char_;
using qi::omit;
symbols<char, int> quad;
quad.add
("1", 1)
("2", 2)
("3", 3)
("4", 4)
("NE", 1)
("SE", 2)
("SW", 3)
("NW", 4)
;
std::wstring s = L"N44°30'14.950\"W";
std::wstring out;
int iQuad;
qi::parse(s.begin(), s.end(),
no_case[char_('N')] >> omit[*(qi::char_ - no_case[char_("NSEW")])] >> no_case[char_('W')],
out);
qi::parse(out.begin(), out.end(), quad, iQuad);
return 0;
}
是的,可以用本地变量来完成。
但是,这会将 symbols
降级为常规地图。那么让我们使用那个¹
1。最简单的事情
首先,我会考虑做最简单的事情:
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <iostream>
namespace Rules {
namespace qi = boost::spirit::qi;
qi::rule<std::wstring::const_iterator, int()> quad = qi::no_case [
('N' >> *~qi::char_("EW") >> 'E')[ qi::_val = 1 ] |
('S' >> *~qi::char_("EW") >> 'E')[ qi::_val = 2 ] |
('S' >> *~qi::char_("EW") >> 'W')[ qi::_val = 3 ] |
('N' >> *~qi::char_("EW") >> 'W')[ qi::_val = 4 ]
];
}
int main() {
for (std::wstring const s : {
L"NE", L"SE", L"SW", L"NW",
L"N44°30'14.950\"E",
L"N44°30'14.950\"W",
L"S44°30'14.950\"W",
L"S44°30'14.950\"E",
L"1", L"2", L"3", L"4",
})
{
int iQuad;
auto f = s.begin(), l = s.end();
bool ok = parse(f, l, Rules::quad, iQuad);
if (ok)
std::wcout << L"Parsed: '" << s << L"' -> " << iQuad << L"\n";
else
std::wcout << L"Parse failed '" << s << L"'\n";
if (f!=l)
std::wcout << L"Remaining unparsed: '" << std::wstring(f,l) << L"'\n";
}
}
打印
Parsed: 'NE' -> 1
Parsed: 'SE' -> 2
Parsed: 'SW' -> 3
Parsed: 'NW' -> 4
Parsed: 'N44?30'14.950"E' -> 1
Parsed: 'N44?30'14.950"W' -> 4
Parsed: 'S44?30'14.950"W' -> 3
Parsed: 'S44?30'14.950"E' -> 2
Parse failed '1'
Remaining unparsed: '1'
Parse failed '2'
Remaining unparsed: '2'
Parse failed '3'
Remaining unparsed: '3'
Parse failed '4'
Remaining unparsed: '4'
如果你想让数字也解析,只需添加
qi::rule<std::wstring::const_iterator, int()> quad = qi::no_case [
(qi::int_(1) | qi::int_(2) | qi::int_(3) | qi::int_(4)) [ qi::_val = qi::_1 ] |
('N' >> *~qi::char_("EW") >> 'E')[ qi::_val = 1 ] |
('S' >> *~qi::char_("EW") >> 'E')[ qi::_val = 2 ] |
('S' >> *~qi::char_("EW") >> 'W')[ qi::_val = 3 ] |
('N' >> *~qi::char_("EW") >> 'W')[ qi::_val = 4 ]
];
All this can be optimized, but I'll venture the guess that it's more efficient than anything based on symbol
and 2-phase parse
2。使用地图查找
只需...使用地图:
template <typename It> struct MapLookup : qi::grammar<It, int()> {
MapLookup() : MapLookup::base_type(start) {
namespace px = boost::phoenix;
start = qi::as_string [
qi::char_("1234") |
qi::char_("nsNS") >> qi::omit[*~qi::char_("weWE")] >> qi::char_("weWE")
] [ qi::_val = px::ref(_lookup)[qi::_1] ];
}
private:
struct ci {
template <typename A, typename B>
bool operator()(A const& a, B const& b) const { return boost::ilexicographical_compare(a, b); }
};
std::map<std::string, int, ci> _lookup = {
{ "NE", 1 }, { "SE", 2 }, { "SW", 3 }, { "NW", 4 },
{ "1" , 1 }, { "2", 2 }, { "3", 3 }, { "4", 4 } };
qi::rule<It, int()> start;
};
也看到了Live On Coliru
3。优化它
qi::symbol
使用 Tries。你可能认为那更快。实际上,它的查找速度非常快。但不是在非常小的键组上。在基于节点的容器上。使用动态分配的临时密钥。
换句话说,我们可以做得更好:
template <typename It> struct FastLookup : qi::grammar<It, int()> {
using key = std::array<char, 2>;
FastLookup() : FastLookup::base_type(start) {
namespace px = boost::phoenix;
start =
qi::int_ [ qi::_pass = (qi::_1 > 0 && qi::_1 <= 4), qi::_val = qi::_1 ] |
qi::raw [
qi::char_("nsNS") >> qi::omit[*~qi::char_("weWE")] >> qi::char_("weWE")
] [ qi::_val = _lookup(qi::_1) ];
}
private:
struct lookup_f {
template <typename R> int operator()(R const& range) const {
using key = std::tuple<char, char>;
static constexpr key index[] = { key {'N','E'}, key {'S','E'}, key {'S','W'}, key {'N','W'}, };
using namespace std;
auto a = std::toupper(*range.begin());
auto b = std::toupper(*(range.end()-1));
return 1 + (find(begin(index), end(index), key(a, b)) - begin(index));
}
};
boost::phoenix::function<lookup_f> _lookup;
qi::rule<It, int()> start;
};
¹ 如果您坚持可以在自己的代码中使用符号
从代码中可以看出,我获取了一次解析的输出,并在第二次解析中使用它从符号中查找数字。 作为一条规则,我该如何做到这一点?查看文档并进行大量搜索让我相信这可以通过本地变量来完成,但我无法弄清楚如何在该变量上使用我的符号四边形。
int main()
{
using boost::phoenix::ref;
using qi::_1;
using qi::_val;
using qi::no_case;
using qi::_a;
using qi::symbols;
using qi::char_;
using qi::omit;
symbols<char, int> quad;
quad.add
("1", 1)
("2", 2)
("3", 3)
("4", 4)
("NE", 1)
("SE", 2)
("SW", 3)
("NW", 4)
;
std::wstring s = L"N44°30'14.950\"W";
std::wstring out;
int iQuad;
qi::parse(s.begin(), s.end(),
no_case[char_('N')] >> omit[*(qi::char_ - no_case[char_("NSEW")])] >> no_case[char_('W')],
out);
qi::parse(out.begin(), out.end(), quad, iQuad);
return 0;
}
是的,可以用本地变量来完成。
但是,这会将 symbols
降级为常规地图。那么让我们使用那个¹
1。最简单的事情
首先,我会考虑做最简单的事情:
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <iostream>
namespace Rules {
namespace qi = boost::spirit::qi;
qi::rule<std::wstring::const_iterator, int()> quad = qi::no_case [
('N' >> *~qi::char_("EW") >> 'E')[ qi::_val = 1 ] |
('S' >> *~qi::char_("EW") >> 'E')[ qi::_val = 2 ] |
('S' >> *~qi::char_("EW") >> 'W')[ qi::_val = 3 ] |
('N' >> *~qi::char_("EW") >> 'W')[ qi::_val = 4 ]
];
}
int main() {
for (std::wstring const s : {
L"NE", L"SE", L"SW", L"NW",
L"N44°30'14.950\"E",
L"N44°30'14.950\"W",
L"S44°30'14.950\"W",
L"S44°30'14.950\"E",
L"1", L"2", L"3", L"4",
})
{
int iQuad;
auto f = s.begin(), l = s.end();
bool ok = parse(f, l, Rules::quad, iQuad);
if (ok)
std::wcout << L"Parsed: '" << s << L"' -> " << iQuad << L"\n";
else
std::wcout << L"Parse failed '" << s << L"'\n";
if (f!=l)
std::wcout << L"Remaining unparsed: '" << std::wstring(f,l) << L"'\n";
}
}
打印
Parsed: 'NE' -> 1
Parsed: 'SE' -> 2
Parsed: 'SW' -> 3
Parsed: 'NW' -> 4
Parsed: 'N44?30'14.950"E' -> 1
Parsed: 'N44?30'14.950"W' -> 4
Parsed: 'S44?30'14.950"W' -> 3
Parsed: 'S44?30'14.950"E' -> 2
Parse failed '1'
Remaining unparsed: '1'
Parse failed '2'
Remaining unparsed: '2'
Parse failed '3'
Remaining unparsed: '3'
Parse failed '4'
Remaining unparsed: '4'
如果你想让数字也解析,只需添加
qi::rule<std::wstring::const_iterator, int()> quad = qi::no_case [
(qi::int_(1) | qi::int_(2) | qi::int_(3) | qi::int_(4)) [ qi::_val = qi::_1 ] |
('N' >> *~qi::char_("EW") >> 'E')[ qi::_val = 1 ] |
('S' >> *~qi::char_("EW") >> 'E')[ qi::_val = 2 ] |
('S' >> *~qi::char_("EW") >> 'W')[ qi::_val = 3 ] |
('N' >> *~qi::char_("EW") >> 'W')[ qi::_val = 4 ]
];
All this can be optimized, but I'll venture the guess that it's more efficient than anything based on
symbol
and 2-phase parse
2。使用地图查找
只需...使用地图:
template <typename It> struct MapLookup : qi::grammar<It, int()> {
MapLookup() : MapLookup::base_type(start) {
namespace px = boost::phoenix;
start = qi::as_string [
qi::char_("1234") |
qi::char_("nsNS") >> qi::omit[*~qi::char_("weWE")] >> qi::char_("weWE")
] [ qi::_val = px::ref(_lookup)[qi::_1] ];
}
private:
struct ci {
template <typename A, typename B>
bool operator()(A const& a, B const& b) const { return boost::ilexicographical_compare(a, b); }
};
std::map<std::string, int, ci> _lookup = {
{ "NE", 1 }, { "SE", 2 }, { "SW", 3 }, { "NW", 4 },
{ "1" , 1 }, { "2", 2 }, { "3", 3 }, { "4", 4 } };
qi::rule<It, int()> start;
};
也看到了Live On Coliru
3。优化它
qi::symbol
使用 Tries。你可能认为那更快。实际上,它的查找速度非常快。但不是在非常小的键组上。在基于节点的容器上。使用动态分配的临时密钥。
换句话说,我们可以做得更好:
template <typename It> struct FastLookup : qi::grammar<It, int()> {
using key = std::array<char, 2>;
FastLookup() : FastLookup::base_type(start) {
namespace px = boost::phoenix;
start =
qi::int_ [ qi::_pass = (qi::_1 > 0 && qi::_1 <= 4), qi::_val = qi::_1 ] |
qi::raw [
qi::char_("nsNS") >> qi::omit[*~qi::char_("weWE")] >> qi::char_("weWE")
] [ qi::_val = _lookup(qi::_1) ];
}
private:
struct lookup_f {
template <typename R> int operator()(R const& range) const {
using key = std::tuple<char, char>;
static constexpr key index[] = { key {'N','E'}, key {'S','E'}, key {'S','W'}, key {'N','W'}, };
using namespace std;
auto a = std::toupper(*range.begin());
auto b = std::toupper(*(range.end()-1));
return 1 + (find(begin(index), end(index), key(a, b)) - begin(index));
}
};
boost::phoenix::function<lookup_f> _lookup;
qi::rule<It, int()> start;
};
¹ 如果您坚持可以在自己的代码中使用符号