c ++从流中提取double [英] c++ extracting double from stream

查看:166
本文介绍了c ++从流中提取double的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I've a funny problem with school exercise. I am given latitude and longitude and I have to ensure that it is in right format: \(\d+\.\d+[NS], \d\+.\d+[EW]\). So as you can see I have to check the . is 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;

> lon 提取。0E部分(这是一个有效的double),但我需要它被提取到EW。

the >> lon extracts ".0E" part (which is a valid double) but i need it to be extracted to EW.

我想出的解决方案是用一些其他字母替换 E ,这可能会工作,但不是真的很好的一块代码。

Only solution I came up with is replace the E with some other letter, which would probably work, but isn't really nice piece of code.

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

Is there some other more elegant solution to this problem?

PS:正则表达式允许:D

PS: You guessed right, no regular expressions allowed :D

推荐答案

您的问题不是你的想法。 .0E 不是有效的浮点数字
,即使在科学记数法中。它缺少任何指数。

Your problem is not what you think. .0E is not a valid floating point numeral even in scientific notation. It lacks any exponent.

发生的是,流解析器提交解释
.0E 作为科学浮点数字,当它到达 E 时,
找不到指数;认为其解释是伪造的;将0分配给
目标double,并设置 iss 中的 failbit ,因此无法从
流是可能的。您可以通过将 0.0E 更改为 1.1E
测试 iss lon 。你试试
仍然会发现 lon code>设置为0而不是0.1,并且 iss.fail()== true

What is happening is that the stream parser is commited to the interpretation of .0E as a scientfic floating point numeral by the time it reaches the E, then finds no exponent; concludes that its interpretation is falsified; assigns 0 to the target double and set the failbit in iss, so no further extraction from the stream is possible. You could verify this by changing 0.0E to 1.1E and testing iss.fail() right after the attempted extraction of lon. You would still find that lon is set to 0, not 0.1, and that iss.fail() == true.

我相信在上下文中没有办法避免这种情况。 w.fE w =整数部分, code> f =小数部分)
如果您试图提取 wf 或只是 .f 转换为具有>> 的浮点变量。
你不在格式化的>> 提取在这个角落case
的舒适区,将需要得到fiddly。事实上,给定的模式 \(\d + \\),需要不会从
单独弹出 w.fE \\d + [NS],\d\ + .\d + [EW] \)
那个
你告诉我们引用必须满足。 \d + \.\d + [NS] 部分也太
严格> double>> char >> [unsigned | int]>> double>> char
整数或浮点提取器将消耗领导 + -
和浮点提取器不会坚持存在一个
小数点或在它之前和之后的非零计数的数字。

I believe there is no way to avoid this in the context w.fE (w = whole part, f = fractional part) if you attempt to extract either the w.f or just the .f into a floating point variable with >>. You are out of the comfort zone of formatted >> extraction in this corner case and will need to get fiddly. And in fact that need does not spring solely from the w.fE corner case, given the pattern \(\d+\.\d+[NS], \d\+.\d+[EW]\) that you tell us the references must satisfy. The \d+\.\d+[NS] part is also too exacting for >> double >> char or for >> [unsigned|int] >> double >> char: the integral or floating point extractors would consume a leading + or - and the floating point extractors would not insist on the presence of a decimal point or on non-zero counts of digits both before and after it.

解析你的地图引用(没有正则表达式的帮助)的麻烦会让
提示我为他们创建一个 map_ref 类,这样你可以试试从输入流(可能也从字符串)中提取
map_ref ;可以询问 map_ref
是否是好的或坏的(例如在尝试提取之后),并且可以插入
a格式化

The fiddliness of parsing your map references (without aid of regexes) would prompt me to make a map_ref class for them, such that you can try to extract a map_ref from an input stream (probably also from a string); can ask a map_ref whether it is a good or bad one (e.g. after an attempted extraction), and can insert a formatted map_ref into an output stream.

这是一个类的草图:

#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 的年龄必须为<= 90北或南,经度必须<= 180东或西。

The class incorporates the obvious constraints that the latitude in a good map_ref shall be <= 90 North or South, and that the longitude shall be <= 180 East or West.

read(std :: istream&)方法根据您的模式解析映射引用
,略微放宽0个或更多个空格是可以接受的后逗号。
请注意,如果
的纬度或经度包含的数字超过可以无变化的
,则会将解析的地图引用分类为 c> double
(即超过 std :: numeric_limits< double> :: digits10
== 15)

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

p>

The method:

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

在输出流中表示纬度和经度的精确度,如 std :: set_precision(precision)
接受默认 4 。如果你将它设置为0,那么你将丢失所有的小数位数
在输出,所以不要这样做。

lets you specify the precision with which the latitude and longitude will be represented in the output stream, as per std::set_precision(precision) or accept the default 4. If you set it to 0, then you'll lose all decimal places on output, so don't do that.

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

The class comes with overloads of the global operators >> and << for formatted extraction and insertion of map_refs. They delegate respectively to map_ref::read and map_ref::write, with the default precision in the latter case; so for any other output precision, invoke map_ref::write directly.

map_ref 解析,您可以附加以下内容:

For some tests of map_ref parsing, you can append the 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 << "): 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;
}

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

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

这篇关于c ++从流中提取double的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆