c ++从流中提取double

时间:2015-04-01 01:18:23

标签: c++ iostream

我在学校运动时遇到了一个有趣的问题。我获得了纬度和经度,我必须确保它的格式正确:\(\d+\.\d+[NS], \d\+.\d+[EW]\)。所以你可以看到我必须检查. 在那里。

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;

>> lon提取".0E"部分(这是有效的双倍),但我需要将其提取到EW。

我提出的唯一解决方案是将E替换为其他一些字母,这可能会起作用,但不是很好的代码。

这个问题还有其他更优雅的解决方案吗?

PS:你猜对了,不允许正则表达式:D

3 个答案:

答案 0 :(得分:1)

你的问题不是你的想法。 .0E不是有效的浮点数 即使是科学记数法。它没有任何指数。

发生的事情是流解析器被提交给解释 当.0E到达E时,failbit作为科学浮点数 找不到指数;得出结论认为其解释是伪造的;指定0到 目标加倍并在iss中设置0.0E,因此无法从中进一步提取 流是可能的。您可以通过将1.1E更改为iss.fail()来验证这一点 在尝试提取lon.之后立即测试lon 仍然发现iss.fail() == true设置为0,而不是0.1,w.fE

我认为在上下文wf =整个部分,w.f =小部分)中无法避免这种情况 如果您尝试使用.f>>>>提取到浮点变量中。 在这个角落的情况下,你已经超出了格式化w.fE提取的舒适区域 并且需要变得狡猾。事实上,这种需求并非完全来自于 \(\d+\.\d+[NS], \d\+.\d+[EW]\)角点情况,给定模式\d+\.\d+[NS] 你告诉我们引用必须满足。 >> double >> char部分也是如此 严格为>> [unsigned|int] >> double >> char+: 积分或浮点提取器将使用前导-map_ref 并且浮点提取器不会坚持存在 小数点或在它之前和之后的非零数字计数。

解析你的地图引用(没有正则表达式的帮助)的繁琐可能会 提示我为他们制作一个map_ref课程,这样你就可以尝试提取一个 来自输入流的map_ref(可能也来自字符串);可以问map_ref 无论是好的还是坏的(例如在尝试提取之后),并且可以插入 格式化#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); } 到输出流中。

以下是这类课程的草图:

map_ref

该类包含了明显的限制,即纬度良好 read(std::istream &)应<= 90北或南,并且经度应<= 180东或西。

double方法根据您的模式解析地图引用, 略微放松,逗号后可接受0或更多空格。 请注意,如果其中任何一个,它会将已解析的地图引用分类为 bad 纬度或经度包含的数字多于可以无需更改即可呈现的数字 在std::ostream & write(std::ostream & out, std::size_t precision = 4) 中(即超过std::numeric_limits<double>::digits10 == 15)

方法:

4

允许您指定纬度和经度的精度 按照std::set_precision(precision)或表示在输出流中表示 接受默认>>。如果将其设置为0,那么您将丢失所有小数位 在输出上,所以不要这样做。

该类带有全局运算符<<map_ref的重载 格式化提取和插入map_ref::read s。他们委托 分别以map_ref::writemap_ref::write为默认精度 在后一种情况下;因此,对于任何其他输出精度,请调用map_ref 直接

对于#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 << "): Is good, got bad\n"; ++fail; } else { ++pass; std::cout << "Passed (" << tests << "): Is 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 << "): Is bad, got good\n"; ++fail; } else { ++pass; std::cout << "Passed (" << tests << "): Is 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 << "): Is good, got bad\n"; ++fail; } else { ++pass; std::cout << "Passed (" << tests << "): Is 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 << "): Is bad, got good\n"; } else { ++pass; std::cout << "Passed (" << tests << "): Is 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; } 解析的一些测试,您可以附加以下内容:

{{1}}

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

答案 1 :(得分:0)

您实际上是在查看解析问题。 operator>>(istream&, string&)是一个非常简单的解析器,只是在空格上进行标记。

如果你的格式规范足够严格,你应该拒绝(51.5N , 0.0E)(逗号之前的额外空格),那么就不要提取逗号。相反,在提取后直接检查nLat是否包含尾随逗号并删除它。您不再需要>>comma

如果必须在任何地方支持可选空格,可以通过在逗号之前和之后插入额外空格(无条件)来预处理字符串。如果已经有空间,那么现在将有两个空间。没有问题,因为空格跳过会跳过任何数量。

答案 2 :(得分:0)

您可以在输入字符串中将E替换为X(例如),运行代码后,您可以在X中将E替换为EW变量。

char    lBracket, rBracket, comma, NS, EW;
int     nLat, nLon;
double  lat, lon;

std::string inputString = "(51.5N, 0.0E)";
size_t e_pos = inputString.find( 'E' );
if ( e_pos != std::string::npos ) {
    inputString.replace( e_pos, e_pos + 1, 'X' );
}
istringstream iss ( inputString );

iss >> fixed >> lBracket >> nLat >> lat >> NS >> comma >> 
                   nLon >> lon >> EW >> rBracket;

if ( EW == 'X' ) {
    EW = 'E';
}

lat += nLat;
lon += nLon;

更新:抱歉,没有看到您的评论

  

我想出的唯一解决办法就是将E替换为其他一些字母,这可能有用,但不是一段很好的代码。

如果您愿意 - 我将删除答案