1 c++ file-io iostream infinite-loop
此代码接受输入文件中的学生姓名,父亲姓名,滚动号和年龄,并以可显示的方式将其放在输出文件中.
在此代码中,当输入文件的内容为:
Vicky
Mohan
20094567
22 Ricky
Rahul
20091234
21
Run Code Online (Sandbox Code Playgroud)
它工作正常.
但如果它们是:
Vicky
Mohan
20094567
22
Ricky
Rahul
20091234
21
Run Code Online (Sandbox Code Playgroud)
它进入一个无限循环.有什么建议??
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();
Run Code Online (Sandbox Code Playgroud)
这是因为你如何阅读输入.你从来没有真正检查它是否成功.
你需要做例如
while (std::getline(...))
{
...
}
Run Code Online (Sandbox Code Playgroud)
您描述的症状的原因是您正在混合格式化输入getline.还有一个基本问题,你永远不会检查是否有任何输入成功.
真正的问题在线后显现inps >> data
:这些线跳过空白并读取int,而不是更多.特别是,它们'\n'在流中留下任何尾随空格,包括字符.所以在你输入的第二种情况下,在读取之后22,'\n'流中仍有一个,它将终止下一次调用
getline(而不是读取" Ricky",将读取
"").这会导致输入变得不同步,这很快会导致您inps >> data在流定位时执行此操作"Rahul".尝试读取int输入"Rahul"失败的时间,并且失败是粘性的; 它会保留,直到你重置它,所有进一步的尝试都是无操作.因为你已经读过line一次,所以它永远不会变空,你永远循环,什么都不做.
第一个也是最重要的变化是 在输入成功的每个输入之后检查,如果没有则不尝试进一步读取.(如果出现错误,您的文件结构可能无法可靠地重新同步.否则,尝试重新同步并继续这是一个很好的策略,这样您就可以在输入中捕获多个错误.)
您需要做的第二件事是确保'\n'在输入整数时读取完整的行(包括).有两种方法可以做到这一点:经典的方法是使用
getline,然后std::istringstream用线初始化,然后int使用它输入.(这允许额外的错误检查,例如,行中没有额外的垃圾.)或者,您可以调用inps.ignore(
std::numeric_limits<std::streamsize>::max(), '\n' );,它将提取和忽略字符,直到'\n'(也被提取).
编辑:
在重读时,我发现我的文字描述并不是那么清楚,所以这是在逐步探索中发生的事情:
第一次通过循环,一切都按预期工作,
但输入位置紧跟在后面"22"(这是最后一个输入).
该getline在循环的顶部被调用.它将返回"22"该行的末尾之间的所有字符.如果"22"后面紧跟一个新行,这将导致一个空行,终止循环(尽管还有更多数据要读取).如果在"22"(例如空白左右)之后有额外的字符,那么这些将被读作行.
假设有额外的字符,那么你可以将"Ricky"作为父亲的名字,并inps >> data为字符串上的卷号做"Rahul".这会失败,并将流设置为错误条件,这会导致所有进一步的操作都是无操作.
因此,当您下次到达循环顶部时,它getline是一个无操作,之前的内容line未更改,您再次进入循环.再次,再次,因为在您清除错误之前,所有操作都将是无操作.所有变量都保留其旧值.
最简单的解决方案可能是由Neil Kirk在评论中提出的:将整个文件读入std :: vector of lines,并解析这些:
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>()) );
Run Code Online (Sandbox Code Playgroud)
但是,如果你想动态阅读文件(比如因为它可能太大而无法放入内存,或者只是因为它是一个很好的学习练习):
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 );
}
}
}
Run Code Online (Sandbox Code Playgroud)
这非常简洁.它仍然需要额外的错误检查,您可能希望跟踪行号,以便您可以输出任何错误消息.但它应该指出你正确的方向.
EDIT2:
此外,您不希望每次循环都打开输出文件.尝试打开已经打开的std::ofstream
将失败,如上所述,一旦流失败,所有进一步尝试使用它都是无操作.
| 归档时间: |
|
| 查看次数: |
2996 次 |
| 最近记录: |