編集: double の周りにラッパー構造を使用しないようにするために、istream
代わりにラッパー クラス内に an を囲みます。
残念ながら、 に別の入力メソッドを追加することによって生じるあいまいさを回避する方法を理解できませんdouble
。以下の実装では、 の周りにラッパー構造を作成しistream
、ラッパー クラスは入力メソッドを実装します。入力メソッドは否定性を判断してから、double を抽出しようとします。それが失敗すると、解析が開始されます。
編集:エラー状態をよりよくチェックするようにしてくれたseheに感謝します。
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);
}
};
putback
解析ルーチンの実装は、最初に考えていたよりも少し複雑でしたが、文字列全体を処理することは避けたかったのです。
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;
}
このルーチンがデフォルトのdouble
入力に対して行う動作の違いの 1 つ-
は、入力がたとえば の場合、文字が消費されないことです"-inp"
。失敗した場合、"-inp"
は引き続きストリームに残りますがdouble_istream
、通常のistream
のみ"inp"
がストリームに残ります。
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;
私のシステムでの上記のスニペットの出力は次のとおりです。
1 nan inf -inf nan 1.2
編集:「iomanip」のようなヘルパー クラスを追加します。double_imanip
チェーン内に複数回出現するオブジェクトは、トグルのように動作します>>
。
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;
}
そして、次のコードを試してみてください。
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;
}
上記の出力は次のとおりです。
1 nan inf -inf nan 1.2
inf
Drise からの編集:元々含まれていなかった Inf や nan などのバリエーションを受け入れるために、いくつかの編集を行いました。また、 http://ideone.com/qIFVoで見ることができるコンパイルされたデモンストレーションにもしました。