Edit: Pour éviter l'utilisation d'un wrapper de la structure autour d'un double, je vous joins un istream
dans une classe wrapper à la place.
Malheureusement, je suis incapable de comprendre comment éviter l'ambiguïté créée par l'ajout de la méthode d'entrée pour double
. Pour la mise en œuvre ci-dessous, j'ai créé un wrapper de la structure autour d'un istream
, et la classe wrapper met en œuvre la méthode d'entrée. La méthode d'entrée détermine la négativité, puis tente d'en extraire un double. Si cela échoue, il commence une analyse.
Edit: Merci à sehe pour me permettre de vérifier les conditions d'erreur de mieux.
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);
}
};
La routine d'analyse est un peu plus délicat à mettre en œuvre que j'ai d'abord pensé qu'il serait, mais je voulais éviter d'essayer d' putback
toute une chaîne de caractères.
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<double>::infinity(); break;
case 2: x = std::numeric_limits<double>::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;
}
Une différence dans le comportement de cette routine par rapport à la valeur par défaut double
d'entrée, c'est que l' -
personnage n'est pas consommé si l'entrée a été, par exemple, "-inp"
. En cas d'échec, "-inp"
sera toujours dans le flux en double_istream
, mais régulièrement, istream
seulement "inp"
sera laissé dans le flux.
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;
La sortie de l'extrait ci-dessus sur mon système:
1 nan inf -inf nan 1.2
Edit: Ajout d'un "iomanip" comme classe d'aide. Un double_imanip
objet agit comme une bascule lorsqu'il apparaît plus d'une fois dans l' >>
chaîne.
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;
}
Et puis le code suivant pour l'essayer:
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;
}
La sortie de la ci-dessus est:
1 nan inf -inf nan 1.2
inf
Edit de Drise: j'ai fait quelques modifications à accepter des variations telles que Inf et nan ce n'était pas à l'origine inclus. J'ai aussi fait un compilé de démonstration, qui peut être consulté à http://ideone.com/qIFVo.