入力ファイルストリームによって読み取られる入力があります(たとえば):
-365.269511 -0.356123 -Inf 0.000000
std::ifstream mystream;
を使用してファイルからいくつかに読み取るとき
double d1 = -1, d2 = -1, d3 = -1, d4 = -1;
(mystream
がすでに開かれていて、ファイルが有効であると仮定します)、
mystream >> d1 >> d2 >> d3 >> d4;
mystream
は失敗状態です。私は期待します
std::cout << d1 << " " << d2 << " " << d3 << " " << d4 << std::endl;
出力する
-365.269511 -0.356123 -1 -1
。代わりに-365.269511 -0.356123 -Inf 0
を出力したいと思います。
このデータセットは、C++ストリームを使用して出力されました。逆のプロセス(出力を読み取る)を実行できないのはなぜですか?求めている機能を取得するにはどうすればよいですか?
MooingDuckから:
#include <iostream>
#include <limits>
using namespace std;
int main()
{
double myd = std::numeric_limits<double>::infinity();
cout << myd << '\n';
cin >> myd;
cout << cin.good() << ":" << myd << endl;
return 0;
}
入力:inf
出力:
inf
0:inf
また、この問題に関連しているのは、例を示していませんが、NaN
解析です。
私は受け入れられた答えにideoneの完全な解決策を追加しました。また、「Inf」と「nan」のペアリングも含まれています。これは、MatLabなどの他のプログラムから取得される可能性のあるキーワードのバリエーションです。
編集:doubleのラッパー構造の使用を避けるために、代わりにラッパークラス内にistream
を囲みます。
残念ながら、double
に別の入力メソッドを追加することによって生じるあいまいさを回避する方法を理解できません。以下の実装では、istream
の周りにラッパー構造を作成し、ラッパークラスはinputメソッドを実装します。入力メソッドは否定性を判別してから、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 で表示できます。
UpdateBoostSpiritがこの領域のすべての種類の特別な値を処理できることを示す簡単なテストケースを提供しました。以下を参照してください:ブーストスピリット(FTW)。
私が見つけたこの分野の唯一の規範的な情報は、C99標準のセクション7.19.6.1 /7.19.6.2にあります。
残念ながら、最新のC++標準ドキュメント(n3337.pdf)の対応するセクションでは、infinity
、inf
、またはNaN
のサポートが同じように指定されていないようです。 (おそらく、C99/C11仕様を参照する脚注がありませんか?)
2000年に、Apache libstdcxxは バグレポート を受け取りました
_
num_get<>
_ファセットのdo_get()
メンバーは、特別な文字列_[-]inf[inity]
_および_[-]nan
_を考慮に入れていません。ファセットは、そのような文字列に遭遇するとエラーを報告します。許可される文字列のリストについては、C99の7.19.6.1および7.19.6.2を参照してください。
ただし、その後の議論では、(少なくともnamed locale
- sの場合)、実装が特別な値を解析することは実際には違法であることがわかりました。
ルックアップテーブルの文字は「0123456789abcdefABCDEF +-」です。ライブラリの問題221は、それを「0123456789abcdefxABCDEFX +-」に修正します。 「N」はルックアップテーブルに存在しないため、num_get <> :: do_get()のステージ2は文字シーケンス「NaN」の読み取りを許可されていません。
securecoding.cert.org は、解析を回避するために次の「準拠コード」が必須であることを明確に示しています無限大またはNaN。これは、一部の実装が実際にそれをサポートしていることを意味します-作成者が公開されたコードをテストしたことがあると仮定します。
_#include <cmath>
float currentBalance; /* User's cash balance */
void doDeposit() {
float val;
std::cin >> val;
if (std::isinf(val)) {
// handle infinity error
}
if (std::isnan(val)) {
// handle NaN error
}
if (val >= MaxValue - currentBalance) {
// Handle range error
}
currentBalance += val;
}
_
次の簡単な例には、目的の出力があります。
_#include <boost/spirit/include/qi.hpp>
namespace qi = boost::spirit::qi;
int main()
{
const std::string input = "3.14 -inf +inf NaN -NaN +NaN 42";
std::vector<double> data;
std::string::const_iterator f(input.begin()), l(input.end());
bool ok = qi::parse(f,l,qi::double_ % ' ',data);
for(auto d : data)
std::cout << d << '\n';
}
_
出力:
_3.14
-inf
inf
nan
-nan
nan
42
_
C99は、* printf/* scanfの動作に無限大とNaNを含めるように指定していると言いたくなります。 C++ 11は、残念ながらそれを指定していないように見えます(または、名前付きロケールが存在する場合は禁止しているようです)。
次のようなシグネチャを使用して関数を記述します。
std::istream & ReadDouble(std::istream & is, double & d);
その中で、あなたは:
operator>>
を使用してストリームから文字列を読み取りますstd::stod
、boost::lexical_cast
など..変数を文字列に読み込んで解析するだけです。文字列をdouble変数に入れて、文字列のように出力されることを期待することはできません。それが機能すれば、文字列は必要ないからです。
次のようなもの:
string text;
double d;
while(cin >> text)
{
if(text == "Inf") //you could also add it with negative infinity
{
d = std::numeric_limits<double>::infinity();
}
else
{
d = atof(text.c_str());
}
}