iostream - c++ extracting double from stream -


i've funny problem school exercise. given latitude , longitude , have ensure in right format: \(\d+\.\d+[ns], \d\+.\d+[ew]\). can see have check . there.

char    lbracket, rbracket, comma, ns, ew; int     nlat, nlon; double  lat, lon;  istringstream iss ("(51.5n, 0.0e)");  iss >> fixed >> lbracket >> nlat >> lat >> ns >> comma >>                     nlon >> lon >> ew >> rbracket; lat += nlat; lon += nlon; 

the >> lon extracts ".0e" part (which valid double) need extracted ew.

only solution came replace e other letter, work, isn't nice piece of code.

is there other more elegant solution problem?

ps: guessed right, no regular expressions allowed :d

your problem not think. .0e not valid floating point numeral in scientific notation. lacks exponent.

what happening stream parser commited interpretation of .0e scientfic floating point numeral time reaches e, finds no exponent; concludes interpretation falsified; assigns 0 target double , set failbit in iss, no further extraction stream possible. verify changing 0.0e 1.1e , testing iss.fail() right after attempted extraction of lon. still find lon set 0, not 0.1, , iss.fail() == true.

i believe there no way avoid in context w.fe (w = whole part, f = fractional part) if attempt extract either w.f or .f floating point variable >>. out of comfort zone of formatted >> extraction in corner case , need fiddly. , in fact need not spring solely w.fe corner case, given pattern \(\d+\.\d+[ns], \d\+.\d+[ew]\) tell references must satisfy. \d+\.\d+[ns] part exacting >> double >> char or >> [unsigned|int] >> double >> char: integral or floating point extractors consume leading + or - , floating point extractors not insist on presence of decimal point or on non-zero counts of digits both before , after it.

the fiddliness of parsing map references (without aid of regexes) prompt me make map_ref class them, such can try extract map_ref input stream (probably string); can ask map_ref whether or bad 1 (e.g. after attempted extraction), , can insert formatted map_ref output stream.

here sketch such class:

#include <ostream> #include <istream> #include <iomanip> #include <utility> #include <limits>  struct map_ref {     map_ref() = default;     map_ref(char ns, double lat, char ew, double lon)     :   _ns(ns == 'n' || ns == 's' ? ns : '?'),         _ew(ew == 'e' || ew == 'w' ? ew : '?'),         _lat(lat >= 0.0 && lat <= 90.0 ? lat : -1),         _lon(lon >= 0.0 && lon <= 180.0 ? lon : -1),         _good(_ns != '?' && _ew != '?' && _lat != -1 && _lon != -1){}      // todo: string ctor      std::pair<char,double> latitude() const {         return std::make_pair(_ns,_lat);     }      std::pair<char,double> longitude() const {         return std::make_pair(_ew,_lon);     }      // todo: setters, getters, equality etc.      bool good() const {         return _good;     }      bool read(std::istream & in);      std::ostream & write(std::ostream & out, std::size_t precision = 4) const {         return out << std::fixed << std::setprecision(precision) <<              '(' << _lat << _ns << ", " << _lon << _ew << ')';     }      void clear() {         *this = map_ref();     }  private:     double      read_fixed_point(std::istream & in, char & dest, std::string const & delims);     char _ns = '?';     char _ew = '?';     double _lat = -1;     double _lon = -1;     bool _good = false; };  double  map_ref::read_fixed_point(     std::istream & in, char & dest, std::string const & delims) {     std::string s;     unsigned whole_digs = 0, frac_digs = 0;     while(in >> dest &&              dest != '.' && delims.find(dest) == std::string::npos)     {         whole_digs += std::isdigit(dest) != 0;         s += dest;     }     if (dest != '.') {         return -1;     }     s += dest;     while(in >> dest && delims.find(dest) == std::string::npos)     {         frac_digs += std::isdigit(dest) != 0;         s += dest;     }     if (whole_digs == 0 || frac_digs == 0 ||         whole_digs + frac_digs > std::numeric_limits<double>::digits10 ||         s.length() != 1 + whole_digs + frac_digs) {             return -1;     }     return std::stod(s); }   bool map_ref::read(std::istream & in) {     char lparen = 0;     clear();     in >> std::noskipws >> lparen;     if (lparen != '(') {         return _good = false;     }     _lat = read_fixed_point(in,_ns,"ns");     if (_lat < 0.0 || _lat > 90.0) {         return _good = false;     }     char comma = 0;     in >> comma;     if (comma != ',') {         return _good = false;     }     while (std::isspace(in.peek())) {         _ew = in.get();     }     _lon = read_fixed_point(in,_ew,"ew");     if (_lon < 0.0 || _lon > 180.0) {         return _good = false;     }     char rparen = 0;     in >> rparen;     return _good = rparen == ')'; }  std::istream & operator>>(std::istream & in, map_ref & mr) {     mr.read(in);     return in; }  std::ostream & operator<<(std::ostream & out, map_ref const & mr) {     return mr.write(out); } 

the class incorporates obvious constraints latitude in map_ref shall <= 90 north or south, , longitude shall <= 180 east or west.

the read(std::istream &) method parses map references per pattern, slight relaxation 0 or more spaces acceptable after comma. note classifies parsed map reference bad if either latitude or longitude contains more digits can respresented without change in double (i.e. more std::numeric_limits<double>::digits10 == 15)

the method:

std::ostream & write(std::ostream & out, std::size_t precision = 4) 

lets specify precision latitude , longitude represented in output stream, per std::set_precision(precision) or accept default 4. if set 0, you'll lose decimal places on output, don't that.

the class comes overloads of global operators >> , << formatted extraction , insertion of map_refs. delegate respectively map_ref::read , map_ref::write, default precision in latter case; other output precision, invoke map_ref::write directly.

for tests of map_ref parsing, can append following:

#include <iostream> #include <sstream>  static unsigned tests = 0, pass = 0, fail = 0;  static void expect_good(char ns, double lat, char ew, double lon ) {     std::cout << "testing (" << ++tests << ") "          << ns << ',' << lat << ',' << ew << ',' << lon << '\n';     map_ref mr(ns,lat,ew,lon);     if (!mr.good()) {         std::cerr << "failed (" << tests << "): good, got bad\n";         ++fail;     } else {         ++pass;         std::cout << "passed (" << tests << "): good. got \"" << mr << "\"\n";       } }  static void expect_bad(char ns, double lat, char ew, double lon ) {     std::cout << "testing (" << ++tests << ") "          << ns << ',' << lat << ',' << ew << ',' << lon << '\n';     map_ref mr(ns,lat,ew,lon);     if (mr.good()) {         std::cerr << "failed (" << tests << "): bad, got good\n";         ++fail;     } else {         ++pass;         std::cout << "passed (" << tests << "): bad, got bad\n";       } }  static void expect_good(std::string const & s) {     std::cout << "testing (" << ++tests << ") \"" << s << "\"\n";     std::istringstream iss(s);     map_ref mr;     iss >> mr;     if (!mr.good()) {         std::cerr << "failed (" << tests << "): good, got bad\n";         ++fail;     } else {         ++pass;         std::cout << "passed (" << tests << "): good. got \"" << mr << "\"\n";       } }  static void expect_bad(std::string const & s) {     std::cout << "testing (" << ++tests << ") \"" << s << "\"\n";     std::istringstream iss(s);     map_ref mr;     iss >> mr;     if (mr.good()) {         ++fail;         std::cerr << "failed (" << tests << "): bad, got good\n";     } else {         ++pass;         std::cout << "passed (" << tests << "): bad, got bad\n";      } }   int main() {     expect_bad('e',1.00,'s',1.00);     expect_bad('n',-1.00,'w',1.00);     expect_bad('n',90.00,'w',180.01);     expect_bad('s',90.01,'w',180.00);     expect_good('s',90.00,'e',180.00);     expect_good('s',0.0,'e',0.0);     expect_bad("");     expect_bad("1.1n, 2.2w");     expect_bad("(1.1n, 2.2w");     expect_bad("1.1n, 2.2w)");     expect_bad("[1.1n, 2.2w)");     expect_bad("(1.1n, 2.2w(");     expect_bad("(n)");     expect_bad("(n, w)");     expect_bad("(0n, 1w)");     expect_bad("(1.0n, 2w)");     expect_bad("(1.0n, .2w)");     expect_bad("(.01n, 1.2e)");     expect_bad("(1.n, 1.2w)");     expect_bad("(n1.1, e1.2)");     expect_bad("(1.0n, 1.2 w)");     expect_bad("(1.0x, 1.2w)");     expect_bad("(1.0n, 1.2z)");     expect_bad("(+1.0n, 1.2e)");     expect_bad("(1.+0n, 1.2e)");     expect_bad("(1.0n, -1.2e)");     expect_bad("(1.0n, 1.-2e)");     expect_bad("(1.1n, 2.3.4e)");     expect_bad("(0.0nn, 0.0e)");     expect_bad("(0.0n, 0.0ew)");     expect_bad("(0.0 1n, 0.0e)");     expect_bad("(0.01n, 0 2.0e)");     expect_bad("(0 .01n, 2.0e)");     expect_bad("(0.01n, 2. 0e)");     expect_bad("(12.34567890123456n, 2.0e)");     expect_good("(0.0n, 0.0e)");     expect_good("(1.0n,1.2w)");     expect_good("(01.0n,01.2w)");     expect_good("(1.0n,  1.2w)");     expect_good("(0.123456789n, 0.000000001e)");     expect_good("(0.000000001s, 0.123456789w)");     expect_good("(0.123456789n, 0.000000001w)");     expect_good("(0.000000001s, 0.123456789e)");     expect_good("(1.1n, 12.3456789012345e)");     expect_bad("(0.1e, 0.1n)");     expect_bad("(0.1w, 0.1s)");     expect_bad("(0.1w, 0.1n)");     expect_bad("(0.1e, 0.1s)");     expect_good("(90.0n, 180.0e)");     expect_good("(90.0s, 180.0w)");     expect_good("(90.0n, 180.0w)");     expect_good("(90.0s, 180.0e)");     expect_bad("(90.000001n, 180.0e)");     expect_bad("(90.000001s, 180.0w)");     expect_bad("(90.0n, 180.000001w)");     expect_bad("(90.0s, 180.000001e)");     std::cout << "tests: " << tests << std::endl;     std::cout << "passed: " << pass << std::endl;     std::cout << "failed: " << fail << std::endl;       return 0; } 

(gcc 4.9.2/clang 3.6, -std=c++11)


Comments

Popular posts from this blog

cakephp - simple blog with croogo -

How to group boxplot outliers in gnuplot -

bash - Performing variable substitution in a string -