49

入力ファイルストリームによって読み取られる入力があります(たとえば):

-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

参照:http://ideone.com/jVvei

また、この問題に関連しているのはNaN、例を示していませんが、構文解析です。

私は受け入れられた答えにideoneの完全な解決策を追加しました。また、「Inf」と「nan」のペアリングも含まれています。これは、MatLabなどの他のプログラムから取得される可能性のあるキーワードのバリエーションです。

4

6 に答える 6

13

更新ブースト スピリットがこの領域であらゆる種類の特別な値を処理できることを示す簡単なテスト ケースを提供しました。以下を参照してください:ブーストスピリット (FTW) .

標準

この分野で私が見つけた唯一の規範的な情報は、C99 標準のセクション 7.19.6.1/7.19.6.2 にあります。

悲しいことに、最新の C++ 標準ドキュメント (n3337.pdf) の対応するセクションでは、infinity, infand または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;
}

ブーストスピリット(FTW)

次の簡単な例では、目的の出力が得られます。

#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

要約/TL;DR

私は、C99 が *printf/*scanf の動作を無限大NaNを含めるように指定していると言いがちです。C++11 では、悲しいことにそれを指定していないようです (または、名前付きロケールが存在する場合は禁止しています)。

于 2012-07-10T19:55:34.050 に答える
9

次のようなシグネチャを持つ関数を記述します。

std::istream & ReadDouble(std::istream & is, double & d);

その中で、あなたは:

  1. を使用してストリームから文字列を読み取りますoperator>>
  2. さまざまな方法のいずれかを使用して、文字列をdoubleに変換してみてください。std::stod、、boost::lexical_castなど..。
  3. 変換が成功した場合は、doubleを設定してストリームを返します。
  4. 変換が失敗した場合は、文字列が「inf」や「INF」などと等しいかどうかをテストします。
  5. テストに合格した場合は、doubleを無限大に設定し、ストリームを返します。それ以外の場合は、次のようにします。
  6. テストが失敗した場合は、ストリームに失敗ビットを設定して返します。
于 2012-07-10T19:54:28.953 に答える
9

編集: double の周りにラッパー構造を使用しないようにするために、istream代わりにラッパー クラス内に an を囲みます。

残念ながら、 に別の入力メソッドを追加することによって生じるあいまいさを回避する方法を理解できませんdouble。以下の実装では、 の周りにラッパー構造を作成しistream、ラッパー クラスは入力メソッドを実装します。入力メソッドは否定性を判断してから、double を抽出しようとします。それが失敗すると、解析が開始されます。

編集:エラー状態をよりよくチェックするようにしてくれたseheに感謝します。

struct double_istream {
    std::istream &in;

    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 = &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で見ることができるコンパイルされたデモンストレーションにもしました。

于 2012-07-10T20:40:03.873 に答える
4

変数を文字列に読み込んで解析するだけです。文字列を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());
    }
}
于 2012-07-10T19:39:12.447 に答える
1

質問はかなり古いですが、私の目的に最も適したソリューションに貢献したいと思います。ブーストの使用を検討しているが、ブースト スピリットがやり過ぎのように思われる場合は、次のようにboost::math::nonfinite_num_putおよびboost::math::nonfinite_num_getロケール ファセット1を使用してみてください。

#include <boost/math/special_functions/nonfinite_num_facets.hpp>
#include <iostream>
#include <limits>
#include <sstream>

int main()
{
  std::locale default_locale;
  std::locale tmp_locale(default_locale,
      new boost::math::nonfinite_num_put<char>());
  std::locale upgraded_locale(default_locale,
      new boost::math::nonfinite_num_get<char>());

  double inf = std::numeric_limits<double>::infinity();

  std::stringstream out_s;
  out_s.imbue(upgraded_locale);
  out_s << inf;
  std::cout << out_s.str() << std::endl;
  std::stringstream in_s(out_s.str());
  in_s.imbue(upgraded_locale);
  double check_inf;
  in_s >> check_inf;
  std::cout << (inf == check_inf ? "nice" : "not nice") << std::endl;
  return 0;
}

出力:

inf
nice
于 2020-04-05T09:48:42.130 に答える
0

特定の実装では明らかにそれらを正しく処理できないため、カスタム抽出関数を作成する必要があります。

于 2012-07-10T19:38:55.237 に答える