I need to parse a string into a boolean value; in particular
if the string is ack I want the parser sets a boolean variable with true;
if the string is noack I want the parser sets a boolean variable with false;
Note that I do not want to use the return value of the function qi::parse; I want the qi::parse function sets a boolean variable given as argument. Something like the following (not compiling) code:
#include <boost/spirit/include/qi.hpp>
#include <iostream>
#include <string>
using namespace boost::spirit;
int main() {
bool b;
std::string s("ack");
auto it = s.begin();
if (qi::parse(it, s.end(), < need a rule here... >, b))
std::cout << "Value: " << std::boolalpha << b << std::endl;
return 0;
}
Thanks a lot in advance.
The simplest approach that comes to mind is "noack" >> qi::attr(false) and its complement. Add an eoi to ensure full input is parsed:
Live On Coliru
#define BOOST_SPIRIT_DEBUG
#include <boost/spirit/include/qi.hpp>
#include <iostream>
#include <string>
namespace qi = boost::spirit::qi;
int main() {
for (std::string const s : {"ack","noack","oops","ack 2",""}) {
bool b;
if (qi::parse(s.begin(), s.end(), ("ack" >> qi::attr(true) | "noack" >> qi::attr(false)) >> qi::eoi, b))
std::cout << "'" << s << "' -> " << std::boolalpha << b << std::endl;
else
std::cout << "'" << s << "' -> FAIL\n";
}
}
Prints
'ack' -> true
'noack' -> false
'oops' -> FAIL
'ack 2' -> FAIL
'' -> FAIL
BONUS:
Use symbol for more elegance:
Live On Coliru
#include <boost/spirit/include/qi.hpp>
#include <iostream>
#include <string>
namespace qi = boost::spirit::qi;
struct acknoack_type : qi::symbols<char, bool> {
acknoack_type() { this->add("ack", true)("noack", false); }
} static const acknoack;
int main() {
for (std::string const s : {"ack","noack","oops","ack 2",""}) {
bool b;
if (qi::parse(s.begin(), s.end(), acknoack >> qi::eoi, b))
std::cout << "'" << s << "' -> " << std::boolalpha << b << std::endl;
else
std::cout << "'" << s << "' -> FAIL\n";
}
}
Related
I need to parse a key value pair, where key itself is a fixed string lke 'cmd' in the example. Unfortunately qi::lit has no synthesized attribute and qi::char_ parses no fixed string.
Following code does not compile. I would need that result.name == cmd after execution.
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <iomanip>
#include <string>
namespace qi = boost::spirit::qi;
namespace px = boost::phoenix;
struct CommandRuleType
{
std::string name;
int arg;
};
BOOST_FUSION_ADAPT_STRUCT(CommandRuleType, name, arg)
int main() {
qi::rule<std::string::const_iterator, CommandRuleType(), qi::space_type> rule = qi::lit("cmd") >> "=" >> qi::int_;
for (std::string const s : {"cmd = 1" }) {
std::cout << std::quoted(s) << " -> ";
CommandRuleType result;
if (qi::phrase_parse(s.begin(), s.end(), rule, qi::space, result)) {
std::cout << "result: " << result.name << "=" << result.arg << "\n";
} else {
std::cout << "parse failed\n";
}
}
}
qi::lit does not expose an attribute. qi::string does:
rule = qi::string("cmd") >> "=" >> qi::int_;
Live On Coliru
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/qi.hpp>
#include <iomanip>
#include <string>
namespace qi = boost::spirit::qi;
namespace px = boost::phoenix;
struct CommandRuleType {
std::string name;
int arg;
};
BOOST_FUSION_ADAPT_STRUCT(CommandRuleType, name, arg)
int main() {
qi::rule<std::string::const_iterator, CommandRuleType(), qi::space_type>
rule = qi::string("cmd") >> "=" >> qi::int_;
for (std::string const s : { "cmd = 1" }) {
std::cout << std::quoted(s) << " -> ";
CommandRuleType result;
if (qi::phrase_parse(s.begin(), s.end(), rule, qi::space, result)) {
std::cout << "result: " << result.name << "=" << result.arg << "\n";
} else {
std::cout << "parse failed\n";
}
}
}
Prints
"cmd = 1" -> result: cmd=1
Assume I already have a struct that looks like this:
struct LETTER
{
double one;
char[12] two;
double three;
char[12] four;
};
And my inputs are comma separated, for example:
"32,CATSANDDOGS,42,WHAT"
"43,BATANDZEBRAS,23,PARROT"
I've been trying to adapt this example (Spirit Qi : rule for char [5]) to to roll through BOOST_FUSION_ADAPT_STRUCT but haven't had any luck. I tried using std::array as shown here (http://www.boost.org/doc/libs/1_64_0/libs/spirit/example/qi/boost_array.cpp) but I haven't been able to make it work in a struct. Is what I'm trying to do even possible? What am I doing wrong here? I would think this would be the most obvious use case.
Is what I'm trying to do even possible?
I'm going to assume you want to write idiomatic C++ code (which is obviously the target domain for Spirit Qi), so you can use std::string:
Live On Coliru
#include <boost/fusion/adapted.hpp>
#include <boost/fusion/include/io.hpp>
#include <boost/spirit/include/qi.hpp>
#include <iostream>
namespace qi = boost::spirit::qi;
struct Letter {
double one;
std::string two;
double three;
std::string four;
};
BOOST_FUSION_ADAPT_STRUCT(Letter, one, two, three, four)
template <typename Iterator> struct LETTERParser : qi::grammar<Iterator, Letter()> {
LETTERParser() : LETTERParser::base_type(start) {
using namespace qi;
_11c = repeat(11) [char_];
start = skip(space) [ "LETTER" >> double_ >> _11c >> double_ >> _11c ];
}
private:
qi::rule<Iterator, Letter()> start;
qi::rule<Iterator, std::string()> _11c;
};
int main() {
const std::string input("LETTER 42 12345678901 +Inf abcdefghijk ");
using It = std::string::const_iterator;
LETTERParser<It> parser;
Letter example;
It f = input.begin(), l = input.end();
if (phrase_parse(f, l, parser, qi::ascii::space, example)) {
std::cout << "parsed: " << boost::fusion::as_vector(example) << "\n";
std::cout << " example.one: " << example.one << "\n";
std::cout << " example.two: '" << example.two << "'\n";
std::cout << " example.three: " << example.three << "\n";
std::cout << " example.four: '" << example.four << "'\n";
} else {
std::cout << "couldn't parse '" << input << "'\n";
}
if (f != l)
std::cout << "Remaining unparsed input: '" << std::string(f,l) << "'\n";
}
Prints
parsed: (42 12345678901 inf abcdefghijk)
example.one: 42
example.two: '12345678901'
example.three: inf
example.four: 'abcdefghijk'
The code that works is the following:
#include <boost/variant.hpp>
#include <string>
#include <map>
#include <iostream>
int main(int argc, char** argv) {
std::map<std::string, boost::variant<int, std::string> > values;
values["a"] = 10;
values["b"] = "bstring";
values["c"] = "cstring";
for (const auto &p : values) {
std::cout << p.first << " = ";
if (p.second.type() == typeid(std::string)) {
std::cout << boost::get<std::string>( p.second ) << " (found string)" << std::endl;
} else if ( p.second.type() == typeid(int)) {
std::cout << boost::get<int>( p.second ) << " (found int)" << std::endl;
} else if ( p.second.type() == typeid(bool)) {
std::cout << boost::get<bool>( p.second ) << " (found bool)" << std::endl;
} else {
std::cout << " not supported type " << std::endl;
}
}
}
The output (g++ test.cpp -std=c++11):
a = 10
b = bstring
c = cstring
The code that does not work is exactly the same, except the line that defines the std::map
modifying the line of the map definition to:
std::map<std::string, boost::variant<int, std::string, bool> > values;
the output is different:
a = 10
b = c =
The if statement that refers to std::string comparison does not succeed. What is the problem?
In your code, values["b"] = "bstring"; creates a bool value, when both std::string and bool are in the variant type.
A fix is values["b"] = std::string("bstring");.
Or, in C++14:
using namespace std::string_literals;
values["b"] = "bstring"s;
It is a well-known annoyance that string literals better convert to bool than to std::string:
#include <iostream>
#include <string>
void f(std::string) { std::cout << __PRETTY_FUNCTION__ << '\n'; }
void f(std::string const&) { std::cout << __PRETTY_FUNCTION__ << '\n'; }
void f(bool) { std::cout << __PRETTY_FUNCTION__ << '\n'; }
int main() {
f("hello");
}
Outputs:
void f(bool)
I want to parse using the boost spirit the following string:
"{"DeliverNr":7424,"fruits":[["apple","banana","orange", "raspberry"]]}"
but I want to put into vector only three first fruits.
I have the following output:
it: { , dist: 0
Can anybody tell me what am I doing wrong ?
#define BOOST_SPIRIT_USE_PHOENIX_V3
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_fusion.hpp>
#include <boost/spirit/include/phoenix_stl.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/variant/recursive_variant.hpp>
#include <boost/foreach.hpp>
#include <iostream>
#include <vector>
struct SomeStructF
{
std::string str1, str2, str3;
};
using namespace boost::spirit;
qi::rule<std::string::iterator> startRule;
qi::rule<std::string::iterator> endRule;
qi::rule<std::string::iterator, std::string()> stringRule;
qi::rule<std::string::iterator, SomeStructF()> valueRule;
qi::rule<std::string::iterator, std::vector<SomeStructF>()> valuesRule;
char const QUOTATION_MARK{ '"' };
char const OPEN_SQUARE_BRACKET{ '[' };
startRule =
+(qi::char_ - qi::char_(OPEN_SQUARE_BRACKET))
>> qi::char_(OPEN_SQUARE_BRACKET);
endRule = +qi::char_;
stringRule =
QUOTATION_MARK
>> *(qi::char_ - QUOTATION_MARK)
>> QUOTATION_MARK;
valueRule =
OPEN_SQUARE_BRACKET
>> stringRule
>> stringRule
>> stringRule;
valuesRule %=
startRule
>> valueRule
>> endRule;
std::vector<SomeStructF> res;
auto it = data.begin();
if (qi::parse(it, data.end(), valuesRule, res))
{
std::cout << "PARSE succeded" << std::endl;
}
std::cout << "it: " << *it << " , dist: " << std::distance(data.begin(), it) << std::endl;
std::cout << "res size " << res.size() << std::endl;
BOOST_FUSION_ADAPT_STRUCT(
parsers::SomeStructF,
(std::string, str1)
(std::string, str2)
(std::string, str3)
)
It's not particularly clear to me what you are asking. It seems to me it's easy enough to take into acocunt the commas, as you noted:
Live On Coliru
#define BOOST_SPIRIT_DEBUG
#include <iostream>
#include <vector>
#include <string>
#include <iterator>
#include <iomanip>
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
namespace qi = boost::spirit::qi;
struct SomeStructF {
std::string str1, str2, str3;
};
BOOST_FUSION_ADAPT_STRUCT(
SomeStructF,
(std::string, str1)
(std::string, str2)
(std::string, str3)
)
int main()
{
qi::rule<std::string::iterator> startRule;
qi::rule<std::string::iterator> endRule;
qi::rule<std::string::iterator, std::string()> stringRule;
qi::rule<std::string::iterator, SomeStructF()> valueRule;
qi::rule<std::string::iterator, SomeStructF()> valuesRule;
char const QUOTATION_MARK{ '"' };
char const OPEN_SQUARE_BRACKET{ '[' };
startRule =
+(qi::char_ - qi::char_(OPEN_SQUARE_BRACKET))
>> qi::char_(OPEN_SQUARE_BRACKET);
endRule = +qi::char_;
stringRule =
QUOTATION_MARK
>> *(qi::char_ - QUOTATION_MARK)
>> QUOTATION_MARK;
valueRule =
OPEN_SQUARE_BRACKET
>> stringRule >> ','
>> stringRule >> ','
>> stringRule;
valuesRule %=
startRule
>> valueRule
>> endRule;
BOOST_SPIRIT_DEBUG_NODES((startRule)(endRule)(stringRule)(valueRule)(valuesRule));
std::string data = R"({"DeliverNr":7424,"fruits":
[["apple","banana","orange","raspberry"]]})";
std::vector<SomeStructF> res;
auto it = data.begin();
if (qi::parse(it, data.end(), valuesRule, res))
{
std::cout << "PARSE succeded" << std::endl;
std::cout << "res size " << res.size() << std::endl;
for (auto& el : res) {
std::cout << "el: {" << std::quoted(el.str1) << ","
<< std::quoted(el.str2) << ","
<< std::quoted(el.str3) << "}\n";
}
}
else
{
std::cout << "Parse did not succeed\n";
}
if (it != data.end())
std::cout << "Remaining unparsed: '" << std::string(it, data.end()) << "'\n";
}
Prints
PARSE succeded
res size 1
el: {"apple","banana","orange"}
Your grammar (nor your question) shows how you'd like to detect multiple SomeStructF so I can't really know what you want. Perhaps you want to simplify:
Live On Coliru
using It = std::string::const_iterator;
qi::rule<It, std::string()> quoted_ = '"' >> *~qi::char_('"') >> '"';
qi::rule<It, SomeStructF()/*, qi::space_type*/> value_ = quoted_ >> ',' >> quoted_ >> ',' >> quoted_;
using boost::spirit::repository::qi::seek;
BOOST_SPIRIT_DEBUG_NODES((quoted_)(value_));
std::string const data = R"({"DeliverNr":7424,"fruits":[["apple","banana","orange","raspberry"]]}
{"DeliverNr":7435,"botanics":[["pine","maple","oak","pernambucco"]]})";
std::vector<SomeStructF> res;
It it = data.begin();
if (qi::phrase_parse(it, data.end(), *seek["[[" >> value_], qi::space, res)) {
for (auto& el : res) {
std::cout << "el: {" << std::quoted(el.str1) << ","
<< std::quoted(el.str2) << ","
<< std::quoted(el.str3) << "}\n";
}
} else {
std::cout << "Parse did not succeed\n";
}
Which prints
el: {"apple","banana","orange"}
el: {"pine","maple","oak"}
Remaining unparsed: ',"pernambucco"]]}'
User inputs a string in form of
length=10 width=15
The task is to find the length's and width's value in such a string(and assign them to variables). So, how can I find those two numbers? What functions/methods should I use? Can you guys just give me an idea?
Regular expressions are fun and are usually not acceptable as homework solutions for introductory classes.
match[1] and match[2] are the numerical portion of the string that you are interested in. You'll probably want to pass them to stoi() if you need to manipulate them as integers.
Code
#include <iostream>
#include <regex>
int main() {
std::string s("length=10 width=15");
std::regex re("length=([0-9]+) width=([0-9]+)");
std::smatch match;
if (regex_match(s, match, re)) {
std::cout << "length: " << match[1] << "\n";
std::cout << "width: " << match[2] << "\n";
}
}
Output
length: 10
width: 15
use stringstream:
#include <string>
#include <iostream>
#include <sstream>
#include <map>
using namespace std;
int main()
{
stringstream ss;
ss.str("length1=10 width=15 length2=43543545");
map<string, int> resMap;
string key;
int val;
while (ss.peek() != EOF) {
if (isalpha(ss.peek())) {
char tmp[256];
ss.get(tmp,streamsize(256),'=') ;
key = tmp;
} else if (isdigit(ss.peek())) {
ss >> val;
resMap.insert(pair<string, int>(key,val));
} else {
ss.get();
}
}
cout << "result:\n";
for (map<string, int>::iterator it = resMap.begin(); it != resMap.end(); ++it) {
cout << "resMap[" << it->first<< "]=" << it->second << endl;
}
getchar();
return 0;
}