I have some input to be read by a input filestream (for example):
-365.269511 -0.356123 -Inf 0.000000
When I use std::ifstream mystream;>
Edit: To avoid the use of a wrapper structure around a double, I enclose an istream
within a wrapper class instead.
Unfortunately, I am unable to figure out how to avoid the ambiguity created by adding another input method for double
. For the implementation below, I created a wrapper structure around an istream
, and the wrapper class implements the input method. The input method determines negativity, then tries to extract a double. If that fails, it starts a parse.
Edit: Thanks to sehe for getting me to check for error conditions better.
struct double_istream {
std::istream ∈
double_istream (std::istream &i) : in(i) {}
double_istream & parse_on_fail (double &x, bool neg);
double_istream & operator >> (double &x) {
bool neg = false;
char c;
if (!in.good()) return *this;
while (isspace(c = in.peek())) in.get();
if (c == '-') { neg = true; }
in >> x;
if (! in.fail()) return *this;
return parse_on_fail(x, neg);
}
};
The parsing routine was a little trickier to implement than I first thought it would be, but I wanted to avoid trying to putback
an entire string.
double_istream &
double_istream::parse_on_fail (double &x, bool neg) {
const char *exp[] = { "", "inf", "NaN" };
const char *e = exp[0];
int l = 0;
char inf[4];
char *c = inf;
if (neg) *c++ = '-';
in.clear();
if (!(in >> *c).good()) return *this;
switch (*c) {
case 'i': e = exp[l=1]; break;
case 'N': e = exp[l=2]; break;
}
while (*c == *e) {
if ((e-exp[l]) == 2) break;
++e; if (!(in >> *++c).good()) break;
}
if (in.good() && *c == *e) {
switch (l) {
case 1: x = std::numeric_limits::infinity(); break;
case 2: x = std::numeric_limits::quiet_NaN(); break;
}
if (neg) x = -x;
return *this;
} else if (!in.good()) {
if (!in.fail()) return *this;
in.clear(); --c;
}
do { in.putback(*c); } while (c-- != inf);
in.setstate(std::ios_base::failbit);
return *this;
}
One difference in behavior this routine will have over the the default double
input is that the -
character is not consumed if the input was, for example "-inp"
. On failure, "-inp"
will still be in the stream for double_istream
, but for a regular istream
only "inp"
will be left in the the stream.
std::istringstream iss("1.0 -NaN inf -inf NaN 1.2");
double_istream in(iss);
double u, v, w, x, y, z;
in >> u >> v >> w >> x >> y >> z;
std::cout << u << " " << v << " " << w << " "
<< x << " " << y << " " << z << std::endl;
The output of the above snippet on my system is:
1 nan inf -inf nan 1.2
Edit: Adding a "iomanip" like helper class. A double_imanip
object will act like a toggle when it appears more than once in the >>
chain.
struct double_imanip {
mutable std::istream *in;
const double_imanip & operator >> (double &x) const {
double_istream(*in) >> x;
return *this;
}
std::istream & operator >> (const double_imanip &) const {
return *in;
}
};
const double_imanip &
operator >> (std::istream &in, const double_imanip &dm) {
dm.in = ∈
return dm;
}
And then the following code to try it out:
std::istringstream iss("1.0 -NaN inf -inf NaN 1.2 inf");
double u, v, w, x, y, z, fail_double;
std::string fail_string;
iss >> double_imanip()
>> u >> v >> w >> x >> y >> z
>> double_imanip()
>> fail_double;
std::cout << u << " " << v << " " << w << " "
<< x << " " << y << " " << z << std::endl;
if (iss.fail()) {
iss.clear();
iss >> fail_string;
std::cout << fail_string << std::endl;
} else {
std::cout << "TEST FAILED" << std::endl;
}
The output of the above is:
1 nan inf -inf nan 1.2
inf
Edit from Drise: I made a few edits to accept variations such as Inf and nan that wasn't originally included. I also made it into a compiled demonstration, which can be viewed at http://ideone.com/qIFVo.