2017-04-12 24 views
1

私はstdinから(巨大な)入力ストリームを受け取り、フロートのベクトルに読み込むソフトウェアを書いています。私は、ストリームにコンマのような文字が入っていて、それを受け入れることができないか、単に浮動小数点として解析できないものをすべて無視したいと考えています。私は、次の行動に気づいた:私はcinの動作を理解しようとしています

#include <iostream> 
using std::endl; 
using std::cin; 
using std::cout; 

int main (int argc, const char* argv[]){ 
    float val; 
    while (cin) { 
     cin >> val; 
     cout << val << endl; 
    } 
    return 0; 
} 

プリント

このバージョンながら

#include <iostream> 
using std::endl; 
using std::cin; 
using std::cout; 

int main (int argc, const char* argv[]){ 
    float val; 
    while (cin >> val) { 
     cout << val << endl; 
    } 
    return 0; 
} 

プリント

1.4 

このバージョン

echo "1.4, -0.7 890 23e-3" | ./cintest 

を呼び出すときコンマなし

1.4 
0 

、最初の1枚の版画

1.4 
-0.7 
890 
0.023 

秒1枚のプリントが

1.4 
-0.7 
890 
0.023 
0.023 

誰かがここで起こっているかを説明してもらえながら?

+0

iOSストリームのcinを使用せず、lookahead/tokenizingパーサーを記述してください。 –

+1

最初のバージョンは正しく、2番目のバージョンは間違っています。 –

+1

'cin >> floatVar;'は '、'(浮動小数点ではない)を読むことができないので、 '0'を返します(ループを終了します)。 – crashmstr

答えて

3

フロートを解析するためにあなたのコード

while (cin >> val) { 

試行の最初のバージョン、およびその後、チェック。 (具体的には、operator>>を呼び出して抽出を行います。エラーの場合はfailbitに設定し、次にbool conversionを使用してfailbitをテストします)。

ストリーム状態が悪い場合(,をfloatに変換できなかったため)、ループ本体は入力されません。したがって、最初に失敗した変換で終了します。

番目のバージョン

while (cin) { 
    cin >> val; 

チェックストリームの状態が(ちょうど以前変換が成功したかを示しますどの)良好であれば、その後、フロートを解析しようとし、これはチェックせずに成功した場合を想定しています。浮動小数点値を使用する前に、変換後のストリーム状態をチェックする必要があります。この場合、前の繰り返しから残されます。

変換が失敗した場合、fail()が真であるが、eof()が偽である(つまり、ファイルの終わり以外の何らかの理由で変換が失敗した)かどうかを確認する必要があります。この場合、入力を破棄するにはignore()を使用します。空白を必要とするか、次のスペースまで無視するか、1文字を無視してもう一度やり直すことができます。

上記のignoreドキュメントには、正しいエラー処理のサンプルコードが含まれています。私たちは失敗した変換の単一の文字をスキップすることを選択した場合、あなたのコードはなる:カンマまたは無効なデータタイプがある場合に

for(;;) { 
    float val; 
    std::cin >> val; 

    if (std::cin.eof() || std::cin.bad()) { 
     break; 
    } else if (std::cin.fail()) { 
     std::cin.clear(); // unset failbit 
     std::cin.ignore(1); // skip next char 
    } else { 
     std::cout << val << '\n'; 
    } 
} 
2

>>が失敗した場合、結果はとなります。

両方のバージョンでは、値を読み取り、コンマ(またはEOF)に達します。 ,とEOFは>>が解析できる有効な整数ではないため、読み込みは明らかに失敗します。そのため、>>の戻り値(ストリーム自体)はfalseに変換され、最初のバージョンでループが終了します(これがうまくいくはずです)。

しかし、2番目のバージョン(これは通常どのように行うべきかはわかりません)では、どんな値でもvalになります。 C++ 11以前では、valは同じままです。 C++ 11 >>が失敗した場合は0と書いています。

TL; 2番目のバージョンでは、ループが遅くなり、1回のゴミの書き込みが行われます。ストリーム状態が良好であるかどうかを

0

のstd :: CINはSTDのインスタンス化::はistream

ですいずれの点でも、演算子>>は失敗する。したがって、あなたのコードは 'val'の最後の既知の値を出力します。ここにリンクされた

は、第1に、あなたがバグを持っているためです 'のstd ::はistream >>'

http://www.cplusplus.com/reference/istream/istream/operator%3E%3E/

1

のリファレンスです。

書式付き入力operator>>が実際に機能しているかどうかを常に確認する必要があります。 operator>>が失敗した場合

if (cin >> val) { 
     cout << val << endl; 
    } 

だから、このコード:

cin >> val; 
    cout << val << endl; 

は以下のように記述する必要があります。次に、ストリームのフェイルビットの1つを設定し、valに値を入れません。したがって、何も入れられていないので、valを印刷する必要はありません。

これは、2番目のバージョンが読み込みデータが残っていないときにガベージを印刷する理由です。読み取りが失敗し、値を出力します。次に、ループを再試行します(失敗します)。

最初のものが正しく動作します。

while (cin >> val) { 
    cout << val << endl; 
} 

値を読み取るので、ループに入る前に読み込みが有効かどうかを確認します。

関連する問題