[英]Entering in an infinite loop while reading a file
此代码从输入文件接受学生姓名,父亲的姓名,卷号和年龄,并将其以可表述的方式放入输出文件中。
在此代码中,当输入文件的内容为:
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();
这是因为您如何阅读输入。 您永远不会真正检查它是否成功。
你需要做例如
while (std::getline(...))
{
...
}
您描述的症状的原因是您将格式化的输入与getline
混合在一起。 还有一个根本的问题,就是您从不检查任何输入是否成功。
真正的问题在inps >> data
行之后显现出来:这些行跳过空白并读取int
,仅此inps >> data
。 特别是,它们会在流中留下任何尾随空格,包括'\\n'
字符。 因此,在您的第二种输入情况下,在读取22
,流中仍然有一个'\\n'
,它将终止对getline
的下一次调用(而不是读取" Ricky"
,而是读取""
)。 这会导致输入变得不同步,这会在流定位在"Rahul"
时很快导致您处理inps >> data
。 当输入为"Rahul"
时尝试读取int
失败,并且失败是粘滞的; 它会一直保留到您重置它为止,并且所有进一步的尝试都是无操作。 因为您已经读过line
,所以它永远不会变空,并且您将永远循环,无所事事。
第一个也是最重要的更改是在每个输入之后检查输入是否成功,如果没有输入,请不要尝试进一步阅读。 (文件的结构是这样的,如果出现错误,您可能无法可靠地重新同步。否则,尝试重新同步并继续操作是一个好策略,这样您就可以在输入中捕获多个错误。)
您需要做的第二件事是确保在输入整数时阅读完整的一行(包括'\\n'
)。 有两种方法:经典方法是使用getline
,然后使用该行初始化std::istringstream
,并使用此方法输入int
。 (这允许进行其他错误检查,例如,该行中没有其他垃圾。)或者,您可以调用inps.ignore( std::numeric_limits<std::streamsize>::max(), '\\n' );
,它将提取并忽略字符,直到'\\n'
(也将被提取)。
编辑:
重新阅读时,我发现我的文字描述不太清楚,所以这是逐步分解的过程:
第一次通过循环,一切都按预期进行, 但是输入位置紧接在"22"
(这是最后一个输入)之后。
调用循环顶部的getline
。 它将返回"22"
和该行末尾之间的所有字符。 如果"22"
后面紧跟着新行,则这将导致空行,从而终止循环(尽管仍有更多数据要读取)。 如果在"22"
之后还有多余的字符(例如一个空格左右),那么这些字符将被视为该行。
假设有多余的字符,则将“ Ricky”作为父亲的名字,然后对字符串"Rahul"
上的掷骰数进行inps >> data
。 这将失败,并将流设置为错误状态,这将导致所有其他操作变为无操作。
因此,当您下一个到达循环的顶部时, 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 );
}
}
}
这非常简洁。 它仍然需要进行其他错误检查,您可能希望跟踪行号,以便可以将其与任何错误消息一起输出。 但这应该将您指向正确的方向。
编辑2:
另外,您不想每次通过循环都打开输出文件。 尝试打开一个已经打开的std::ofstream
将会失败,如上所述,一旦该流失败,所有进一步使用它的尝试都是无操作的。
更换
for(getline(inps,line);line!="";getline(inps,line))
与
while (getline(inps, line))
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.