C ++ alternative for parsing with sscanf

Assuming my program expects arguments of the form [ 0.562 , 1.4e-2 ] (ie pairs of floats), how should I parse this input in C ++ without regular expressions? I know that there are many angular cases to consider when it comes to user input, but let's assume that this input closely matches the above format (except for additional spaces).

In C, I could do something like sscanf(string, "[%g , %g]", &f1, &f2); to extract two floating point values, which is very compact.

In C ++, this is what I came up with so far:

 std::string s = "[ 0.562 , 1.4e-2 ]"; // example input float f1 = 0.0f, f2 = 0.0f; size_t leftBound = s.find('[', 0) + 1; size_t count = s.find(']', leftBound) - leftBound; std::istringstream ss(s.substr(leftBound, count)); string garbage; ss >> f1 >> garbage >> f2; if(!ss) std::cout << "Error while parsing" << std::endl; 

How can I improve this code? In particular, I am associated with the garbage string, but I don’t know how else to skip between these two values.

+10
c ++ parsing scanf
source share
4 answers

The obvious approach is to create a simple manipulator and its use. For example, a manipulator using a statically provided char to determine if the next character without spaces is this character, and if so, excerpts, it might look like this:

 #include <iostream> #include <sstream> template <char C> std::istream& expect(std::istream& in) { if ((in >> std::ws).peek() == C) { in.ignore(); } else { in.setstate(std::ios_base::failbit); } return in; } 

Then you can use the manipulator this way to extract the characters:

 int main(int ac, char *av[]) { std::string s(ac == 1? "[ 0.562 , 1.4e-2 ]": av[1]); float f1 = 0.0f, f2 = 0.0f; std::istringstream in(s); if (in >> expect<'['> >> f1 >> expect<','> >> f2 >> expect<']'>) { std::cout << "read f1=" << f1 << " f2=" << f2 << '\n'; } else { std::cout << "ERROR: failed to read '" << s << "'\n"; } } 
+7
source share

If you can use a raise, you can use the Spirit.

See

  • From string Live On Coliru (in C ++ 03):

  • Update. And here is the approach if you actually tried to read from the stream (in fact, it is somewhat simpler and integrates very well with your other actions for reading the stream):
    Live on Colira too (c ++ 03)

Although this seems more verbose, Spirit is also much more powerful and type safe than sscanf . And it works on threads.

Also note that inf , -inf , nan will be processed as expected.

Live on coliru

 #include <boost/spirit/include/qi.hpp> #include <boost/spirit/include/qi_match.hpp> #include <sstream> namespace qi = boost::spirit::qi; int main() { std::istringstream ss("[ 0.562 , 1.4e-2 ]"); // example input ss.unsetf(std::ios::skipws); // we might **want** to handle whitespace in our grammar, not needed now float f1 = 0.0f, f2 = 0.0f; if (ss >> qi::phrase_match('[' >> qi::double_ >> ',' >> qi::double_ >> ']', qi::space, f1, f2)) { std::cout << "Parsed: " << f1 << " and " << f2 << "\n"; // default formatting... } else { std::cout << "Error while parsing" << std::endl; } } 
+5
source share

In addition to regular expressions, perhaps something in Boost can be used. But if you cannot use Boost, you can define a face of std::ctype<char> that effectively ignores all unnecessary characters, classifying them as whitespace characters. You can set this cell to a locale and populate it in ss .

+2
source share

Using old school and simplicity:

 std::istringstream inp_str("[ 0.562 , 1.4e-2 ]"); double x; double y; char c; inp_str >> c; // Eat the '[' inp_str >> x; // Input the first ordinate. inp_str >> c >> c; // Eat the space and comma. inp_str >> y; // Input the second ordinate. 

This works because the extract statement skips leading spaces for numeric types.

+1
source share

All Articles