5

次のプログラムでの scanf の動作について混乱しています。scanf は、文字のストリームが出力されるまで、一度入力したように見えますが、再度入力することはありません。

以下のCプログラムで

#include<stdio.h>
int main()
{
    int i, j=0;

    do
    {
        ++j;
        scanf("%d", &i);
        printf("\n\n%d %d\n\n", i, j);
    }
    while((i!=8) && (j<10));  

    printf("\nJ = %d\n", j);
    return 0;
}

ここで、任意の整数プログラムを入力するまでは完全に正常に動作しますが、文字が入力されると、最後に入力された i の値を出力し続け、scanf が次の入力を取得するために (ループが終了したときに j が 10 になるまで) 停止しません。

output::  
1    <-----1st input
1 1
2    <---- 2nd input
2 2
a    <---- character input
2 3  
2 4
2 5
2 6
2 7
2 8
2 9
2 10

J = 10  

C ++でも同じことが起こっています。

#include<iostream>
using namespace std;
int main()
{
    int i, j=0;

    do
    {
        ++j;
        cin>>i;
        cout<<i<<" "<<j<<"\n";
    }
    while((i!=8) && (j<10));

    cout<<"\nj = "<<j<<"\n";
}   


output of c++ program ::  
1     <-----1st input
1 1
2     <-----2nd input
2 2
a    <------ character input
0 3
0 4
0 5
0 6
0 7
0 8
0 9
0 10

j = 10  

C++ での唯一の変更点は、最後の値の代わりに 0 が出力されることです。

ここでプログラムが整数値を期待していることは知っていますが、整数の代わりに文字を入力するとどうなるか知りたいですか? 上記のすべての理由は何ですか?

4

7 に答える 7

6

と入力すると、は文字が読めないタイプのため、読み取りacin >> i失敗します。つまり、ストリームに永久に残ります。iinta

さて、i版画0は別の話です。実際には何でも印刷できます。i読み取りに失敗すると、の内容は定義されません。同様のことが同様に起こりscanfます。

これを書く適切な方法:

do
{
    ++j;
    if (!(cin>>i)) 
    {
       //handle error, maybe you want to break the loop here?
    }
    cout<<i<<" "<<j<<"\n";
}
while((i!=8) && (j<10));

または単にこれ(エラーが発生した場合にループを終了する場合):

int i = 0, j = 0;
while((i!=8) && (j<10) && ( cin >> i) )
{
    ++j;
    cout<<i<<" "<<j<<"\n";
}
于 2012-06-07T16:10:15.453 に答える
5

scanf入力ストリームに変換指定子と一致しない文字がある場合、変換を停止し、問題のある文字を入力ストリームに残します。

これに対処するには、いくつかの方法があります。1 つは、すべてをテキストとして読み取り ( or変換指定子 orを使用)、orを使用scanfして変換を行うことです (私の好みの方法)。%s%[fgetsatoistrtol

scanfまたは、 ;の戻り値を確認することもできます。成功した変換の数が示されます。したがって、scanf("%d", &i);が 0 の場合、入力ストリームに不正な文字があることがわかります。で消費してgetchar()、もう一度試すことができます。

于 2012-06-07T16:12:29.820 に答える
3

ユーザーが有効なものを入力することを期待することはできません。ベスト プラクティスは、入力を文字列に読み取り、それを integer に変換することです。入力が整数でない場合、ユーザーにエラー メッセージを表示できます。

于 2012-06-07T16:11:50.693 に答える
3

問題は、期待されるタイプ( %dscanf で指定され、 で指定されたタイプ) ではない入力を入力すると、入力ストリームが拡張されず、両方の操作がまったく同じデータから同じタイプのデータを抽出しようとすることです。間違った入力 (そして今回も同様に失敗) のため、別の入力を求めることはありません。intcin>>i;

これが起こらないようにするには、両方の操作の戻り値を確認する必要があります (それぞれがどのようにエラーを報告するかについては、マニュアルを参照してください)。エラー発生した場合 (文字を入力したときなど)、エラーをクリアし、無効な入力を消費して再試行する必要があります。C++ では、ユーザーからインタラクティブに入力を取得するstd::gtline()代わりに、intまたはユーザーからの入力を取得する場合でも、行全体を読み取る方が良いことがわかったので、経験したこの「無限」ループに入ります。std::string

于 2012-06-07T16:12:47.353 に答える
1

戻り値を無視しています。マニュアルの内容を参照してくださいscanf(3)

RETURN VALUE
These functions return the number of input items successfully matched and assigned, which can be fewer than provided for, or even zero in the event of an early matching failure.

整数の照合に失敗します。

于 2012-06-07T16:12:49.973 に答える
1

の場合scanf、戻り値をチェックして、入力の変換が機能したかどうかを確認する必要があります。scanf正常にスキャンされた要素の数を返します。変換がうまくいかなかった場合、入力はそのまま残り、別の方法でスキャンするか、単にエラーを報告することができます。例えば:

if (scanf("%d", &i) != 1) {
    char buf[512];
    fgets(buf, sizeof(buf), stdin);
    printf("error in line %d: got %s", j, buf);
    return 0;
}

プログラムでは、入力がそのまま残されているため、同じ入力を読み取ろうとしてループが繰り返されます。

C++ では、failメソッドを使用して失敗をチェックしますが、入力ストリームの失敗状態はスティッキーです。そのため、エラー状態をクリアせずにさらにスキャンすることはできません。

    std::cin >> i;
    if (std::cin.fail()) {
        std::string buf;
        std::cin.clear();
        std::getline(cin, buf);
        std::cout
             << "error in line " << j
             << ": got " << buf
             << std::endl;
        return 0;
    }

プログラムでは、失敗状態をクリアしないため、ループは失敗状態で使用を繰り返すcinため、何もせずに失敗を報告するだけです。

どちらの場合も、最初に入力行を読み取ってから入力行を解析しようとすると、入力を操作する方が簡単で信頼性が高くなることがあります。擬似コード:

while read_a_line succeeds
    parse_a_line

C では、行を読み取る際の問題は、それがバッファーよりも長い場合、それを確認し、複数のfgets呼び出し結果を連結して行を形成する必要があることです。また、行を解析するには、 を使用できますsscanf。これは に似てscanfいますが、文字列に対して機能します。

    if (sscanf(buf, "%d", &i) != 1) {
        printf("error in line %d: got %s", j, buf);
        return 0;
    }

C++ では、書式設定された入力を低レベルですばやく解析するために、sscanf. ただし、ストリーム アプローチを使用する場合は、stringバッファを に変換しistringstreamて入力をスキャンできます。

    std::getline(cin, buf);
    if (std::cin.fail()) {
        break;
    }
    std::istringstream buf_in(buf);
    buf_in >> i;
    if (buf_in.fail()) {
        std::cout << "error in line " << j
             << ": got " << buf
             << std::endl;
        return 0;
    }
于 2012-06-07T16:13:31.347 に答える
1

の戻り値をチェックしてscanf、整数が正しく解析されたかどうかを判断できます (戻り値は =1 である必要があります)。失敗した場合、選択肢があります。ユーザーにエラーを通知して終了するか、次のトークンを読み取り、scanf("%s" ...)おそらく警告とともに回復します。

于 2012-06-07T16:13:35.747 に答える