此代码接受来自输入文件的学生姓名,父亲姓名,掷骰号码和年龄,并以可呈现的方式将其放在输出文件中。
在此代码中,输入文件的内容为:
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();
答案 0 :(得分:6)
这是因为你如何阅读输入。你从来没有真正检查它是否成功。
你需要这样做。
while (std::getline(...))
{
...
}
答案 1 :(得分:5)
您描述的症状的原因是您正在混合
带有getline
的格式化输入。还有一个根本
你永远不会检查是否有任何输入成功的问题。
真正的问题在inps >> data
之后显现出来
lines:这些行跳过空格并读取int
,没有
更多。特别是,他们留下任何尾随的空白,
包括流中的'\n'
字符。所以在你的
第二种情况是输入,在阅读22
之后,仍有
流中的'\n'
,将终止下一次调用
getline
(而非阅读" Ricky"
将会阅读
""
)。这会导致输入变得不同步
很快会导致您在流时执行inps >> data
位于"Rahul"
。尝试阅读int
时
输入"Rahul"
失败,失败是粘性的;它会保留下来
直到你重置它,所有进一步的尝试都是no-ops。以来
你已经读过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
的先前内容将保持不变,而您
再次进入循环。又一次又一次,因为直到你
清除错误,所有操作都将是no-ops。全部
变量保持旧的价值。
最简单的解决方案可能是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>()) );
但是,如果你想动态阅读文件(比如因为它 可能太大而无法融入记忆,或仅仅因为它 一个很好的学习练习):
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
将失败,如上所述,一旦流失败,所有进一步
尝试使用它是无操作。
答案 2 :(得分:2)
替换
for(getline(inps,line);line!="";getline(inps,line))
与
while (getline(inps, line))