1

このコードは、入力ファイルから生徒の名前、父親の名前、ロール番号、年齢を受け取り、出力ファイルに表示可能な方法で配置します。

このコードでは、入力ファイルの内容が次の場合:

Vicky
Mohan
20094567
22   Ricky
Rahul
20091234
21

それは正常に動作します。

しかし、それらが次の場合:

Vicky
Mohan
20094567
22
Ricky
Rahul
20091234
21

無限ループに入ります。助言がありますか??

ifstream inps("input", ios::in);
outs.open("output",ios::app);

string line;
int data,count=1;

for(getline(inps,line);line!="";getline(inps,line))
{
    count++;

    s1.setName(line);
    getline(inps,line);
    s1.setFatherName(line);
    inps >> data;
    s1.setRollNo(data);
    inps >> data;
    s1.setAge(data);

    outs.open("output",ios::app);
    outs << "Student name: " << s1.getName() << endl;
    outs << "Father’s name: " << s1.getFatherName() << endl;

    outs << "Roll number: " << s1.getRollNo() << endl;
    outs << "Age: " << s1.getAge() << endl << endl;
}

inps.close();
outs.close();
4

3 に答える 3

6

それは、入力の読み方が原因です。成功するかどうかを実際に確認することはありません。

あなたは例えばする必要があります

while (std::getline(...))
{
    ...
}
于 2013-08-06T12:02:25.100 に答える
5

あなたが説明する症状の理由は、フォーマットされた入力とgetline. 入力のいずれかが成功するかどうかを決してチェックしないという根本的な問題もあります。

本当の問題は、行の後に現れますinps >> data 。これらの行は、空白をスキップしてint, を読み取ります。特に、文字を含む末尾の空白を'\n'ストリームに残します。したがって、入力の 2 番目のケースでは、 を読み取った後22、まだ'\n'ストリームに があり、次の呼び出しを終了します getline( を読み取る代わりに" Ricky"、 を読み取ります "")。これにより、入力が非同期になり、すぐinps >> dataにストリームが に配置されたときに実行されます"Rahul"int入力が失敗したときにan を読み込もうとしていて"Rahul"、失敗はスティッキーです。リセットするまで残り、それ以降の試行はすべてノーオペレーションです。あなたはすでに何かを読んでいるのでline一度空になることはなく、何もせずに永遠にループします。

最初の、そして最も重要な変更は、すべて の入力の後に入力が成功したことを確認し、失敗した場合はそれ以上読み取ろうとしないことです。(ファイルの構造は、エラーが発生した場合、確実に再同期できない可能性があります。それ以外の場合は、再同期を試行して続行し、入力で複数のエラーをキャッチできるようにすることをお勧めします。)

'\n'2 番目に行う必要があるのは、整数を入力するときに完全な行 ( を含む) を読み取ることです。これを行うには 2 つの方法があります。古典的な方法は、 を使用 し、行でgetlineを初期化し、 this を使用しstd::istringstreamて を入力するintことです。(これにより、追加のエラー チェックが可能になります。たとえば、行に追加のゴミがないことなどです。) または、 を呼び出して、 (これも抽出されるinps.ignore( std::numeric_limits<std::streamsize>::max(), '\n' );まで) 文字を抽出して無視することもできます。'\n'

編集:

読み直してみると、私のテキストの説明はそれほど明確ではないことに気付きました。

  • 初めてループを通過すると、すべてが期待どおりに機能します 、入力位置は"22"(最後の入力であった) のすぐ後ろにあります。

  • getlineループの先頭にある が呼び出されます。"22"その行の と末尾の間のすべての文字を返します。の直後に新しい行が続く場合は"22"、空の行になり、ループが終了します (ただし、読み取るデータはまだあります)。の後に余分な文字がある場合"22"(空白など)、これらは行として読み取られます。

  • 余分な文字があると仮定すると、「リッキー」を父親の名前として読みinps >> data、文字列のロール番号を読みます"Rahul"。これは失敗し、ストリームをエラー状態に設定します。これにより、以降のすべての操作がノーオペレーションになります。

  • そのため、次にループの先頭に到達すると、getlineはノーオペレーションであり、 の以前の内容lineは変更されず、再びループに入ります。繰り返しますが、エラーをクリアするまで、すべての操作はノーオペレーションになるためです。すべての変数は古い値を保持します。

最も簡単な解決策は、おそらく Neil Kirk がコメントで提案したものです。ファイル全体を行の std::vector に読み取り、それらを解析します。

class Line
{
    std::string myContents;
public
    friend std::istream& operator>>( std::istream& source, Line& obj )
    {
        std::getline( source, obj.myContents );
        return source;
    }
    operator std::string() const { return myContents; }
};

// ...
std::vector<Line> lines( (std::istream_iterator<Line>( inps )),
                         (std::istream_iterator<Line>()) );

ただし、ファイルをオンザフライで読み取りたい場合 (たとえば、大きすぎてメモリに収まらない場合や、単に学習に適しているため):

while ( std::getline( inps, line ) && !line.empty() ) {
            //  but do you really what the second condition.
            //  if so, you should probably provide
            //  a function which will ignore whitespace.
    s1.setName( line );
    if ( std::getline( inps, line ) ) {
        s1.setFatherName( line );
    }
    if ( std::getline( inps, line ) ) {
        std::istringstream s( line );
        int data;
        if ( s >> data ) {
            s1.setRollNo( data );
        }
    }
    if ( std::getline( inps, line ) ) {
        std::istringstream s( line );
        int data;
        if ( s >> data ) {
            s1.setAge( data );
        }
    }
}

これは非常に簡潔です。追加のエラー チェックが必要であり、エラー メッセージと共に出力できるように、行番号を追跡する必要があります。しかし、それはあなたを正しい方向に向けるはずです。

EDIT2:

また、ループのたびに出力ファイルを開きたくありません。既に開いているものを開こうとstd::ofstream すると、上記のように失敗します。ストリームが失敗すると、それ以降の使用の試みはすべて no-op になります。

于 2013-08-06T12:52:57.383 に答える
2

交換

for(getline(inps,line);line!="";getline(inps,line))

while (getline(inps, line))
于 2013-08-06T12:04:35.253 に答える