6

このコードを見てください:

#include <iostream>
using namespace std;
int main()
{
    string s;
    int n;
    float x;
again:
    cout << "Please Type this: ABC456 7.8 9 XYZ\n";
    cin >> s >> n >> x >> s;
    cout << "\nDo you want to try Again(y/n)? ";
    char t;
    cin >> t;
    if (t=='y' || t=='Y')
        goto again;
    return 0;
}

「ABC456 7.8 9 XYZ」と入力して Enter キーを押すと、ユーザーに再試行を求める前にプログラムが終了します。入力が間違っていて、型に含まれていないことはわかっていますが、なぜ終了するのですか? そして、そのような出口を避ける方法は?

4

7 に答える 7

7

変化する

cin >> s >> n >> x >> s;

cin >> s >> x >> n >> s;

2番目の入力として入力しています7.8が、浮動小数点変数ではなく整数変数で収集しています。この結果、次のように入力します。

ABC456 7.8 9 XYZ

sgets ABC456ngets 7(int 型であり、入力バッファーにまだ含ま.8 9 XYZ\nれているため)。Nextnを取得.8し、最後にsを取得し"9"ます。これで、入力バッファーが含まXYZ\nれるようになりました。次にtユーザーの選択を取得するために入力を読み込むと、Xに読み込まれ、 ortではないため、ループが終了します。yY

于 2012-04-30T06:18:47.350 に答える
3

の後にデバッグ行を入力しますcin

cout << "s = " << s << "   n = " << n << "   x = " << x;  

そして実行します

Please Type this: ABC456 7.8 9 XYZ
ABC456 7.8 9 XYZ
s = 9   n = 7   x = 0.8

明らかに、最初のABC456が文字列に読み込まれますs。次は整数だったので、のみ7が読み込まれn0.8一部はに読み込まれましfloat xた。これで、次の入力9が再び割り当てられsたため、の最終値sは文字列「9」になります。そして今、の最初の文字が次の文字にXフィードされ、cinそこで割り当てられtます。cout << "\nt = " << t;確認するには、を入力した後に別のデバッグ行を挿入しますt。したがって、値が「X」に割り当てられてifいる場合はfalseであるため、プログラムは終了します。t

代わりに入力ABC456 7.8 9 YZすると、プログラムはもう一度入力を求めますt。これは「Y」になります。

于 2012-04-30T06:28:58.430 に答える
2

ストリーム抽出オペレーター>>が無効な入力に遭遇すると、それ以上入力が抽出されないモードにストリームを置きます。

などのブール値テストを使用してこのモードを検出し、 をif ( cin )使用してリセットできcin.clear()ます。そうすると無効な入力がcinの入力バッファに残るので、 などで何らかの処理をする必要がありますignore

さらに良いことに、抽出演算子が返さcinれるので、抽出中にテストできます。

if ( ! ( cin >> s >> n >> x >> s ) ) {
    cout << "You fail horribly!\n";
    cin.clear();
    cin.ignore( std::numeric_limits< std::streamsize >::max(), '\n' );
}

詳細については、basic_ios のフラグのセマンティクスを参照してください(このサイトには、これとまったく同じ質問がいくつかあると確信しています)。

于 2012-04-30T06:38:52.727 に答える
2

https://stackoverflow.com/a/10379322/924727に答えて、何が起こるかを説明してください。なぜについては、少し哲学に入る必要があります。

C++ ストリーム モデルは、"人間の対話" とは考えられていません。実質的に無限の文字シーケンスを、指定された "変数" に変換されるスペースで区切られた "値" のリストに変換する汎用コンバータです。

「ダイアログの入出力インターリーブ」という概念はありません。入力を次のようにテキストファイルに書き込む場合myinput.txt(正しい入力を使用しない)

ABC456 9 7.8 XYZ
Y
ABC456 5 6.7 XYZ
N

次のようにコマンドプロンプトからプログラムをronします

   myprogram < myinput.txt

あなたのプログラムは実行されます...そして出力を見るために「一時停止」する必要はありません。

プログラムがユーザー入力を待機するために一時停止するのはcin >>、 が原因cinではなく、 が失敗状態ではなく、バッファーが空であり、バッファーが再マップするソースがコンソールであるためです。戻る前に '\n' を待つのはコンソールであり、cin ではありません。

cin >> n呼ばれたら…

  • operator>>関数が呼び出され、それが...
  • ストリーム ロケールから num_get ファセットを取得し、その get 関数を呼び出します...
  • ストリーム バッファsbumpcを繰り返し呼び出して数字を取得し、数値を計算します。
  • バッファにコンテンツがある場合は、その文字を次々に返します。これ以上文字が存在しない場合 (または空の場合) ...
  • バッファーは、オペレーティング システムに低レベル ファイルから読み取るように要求します。
  • ファイルがコンソールの場合、コンソールの内部ライン エディターが呼び出されます。
  • これにより、Enter キーが押されるまで、ユーザーが文字といくつかのコントロール (たとえば、バックスペースなど) を押すことができなくなります。
  • コンソール ライン エディタは、内容を入力 CON ファイルで使用できるようにする行をオペレーティング システムに返します。
  • これは、(読み取り文字を cvt ロケール ファセットに渡した後ですが、これは詳細です) バッファーによって読み取られ、それ自体がいっぱいになります。
  • ここで、展開を返します。

このすべてのメカニズムにより、必要以上に入力した場合、>>別のプログラム行であるかどうかに関係なく、バッファーの内容は次の呼び出しで引き続き使用できるようになります。

適切な「より安全な」解析では、入力が読み取られた後、ストリームの状態がクリアされ、次のコンテンツまで無視される必要があります'\n'。これは通常、

cin.clear(); 
cin.ignore(numeric_limits<std::streamsize>::max(), '\n');

そのため、入力されたものはすべて破棄され、次cin>>はデータのないバッファー (「開始スペース」としてトリミングされた , のみ) が見つかり'\n'、コンソールが再び行編集モードになります。

于 2012-04-30T08:14:42.800 に答える
1

cin.clear() を呼び出すときは、同時に cin.sync() を呼び出す必要があります。

于 2012-04-30T07:01:11.040 に答える
1

ストリームがエラーを検出すると、エラー状態になり、それ以降の入力の試行はすべてノーオペレーションになります。読み取りが成功したかどうかを最初にテストせずに、ストリームによって読み取られた変数にアクセスすることは未定義の動作であるため、少なくとも理論的には、プログラムは何でもできます。(実際には、初期化されていない変数の型が である場合、charリスクはランダムな値だけです。)

行指向の入力を読み取る場合、最善の解決策は を使用すること std::getlineです。次に、入力された文字列を使用して を構築し std::istringstream、行を解析します。これにより、入力ストリームは良好な状態のままになり、次の入力のためにすでに同期されています。私は次のようなものを使用します:

void
getInput()
{
    std::string line;
    std::cout << "Please type this: ABC456 7.8 9 XYZ" << std::endl;
    std::getline( std::cin, line );
    if ( ! std::cin ) {
        //  Very unexpected error... 
        throw SomethingSerious();
    }
    std::string s;
    int n;
    float f;
    std::istringstream toParse( line );
    toParse >> s >> f >> n >> s;
    if ( ! toParse ) {
        //  Input incorrect...
    }
}

bool
tryAgain()
{
    std::cout << "Do you want to try again (y/n)? ";
    std::string line;
    std::getline( std::cin, line );
    return line.size() == 1 && (line[0] == 'y' || line[0] == 'Y');
        //  But I'd be more tolerant with regards to spaces...
}

bool
oneInput()
{
    getInput();
    return tryAgain();
}

int
main()
{
    while ( oneInput() ) {
    }
    return 0;
}
于 2012-04-30T08:10:06.230 に答える
1

プログラムは、間違ったデータ型を一緒にしようとすると、問題または例外に遭遇します。おそらく、cin の >> 演算子のドキュメントを見て、データ型の間違った入力にヒットしたときの動作の詳細を探し、cin>> 行と入力を調べて、これがどこにあるかを調べてください。これにより、入力が正しく処理されていることを検証できます

于 2012-04-30T06:18:55.260 に答える