あなたが説明する症状の理由は、フォーマットされた入力と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 になります。