您描述的症状的原因是您将格式化输入与getline
. 还有一个基本问题是您永远不会检查任何输入是否成功。
真正的问题出现在这些行之后inps >> data
:这些行跳过空格并读取一个int
,仅此而已。特别是,它们会'\n'
在流中留下任何尾随空格,包括字符。因此,在您的第二种输入情况下,在read 之后22
,流中仍然有 a '\n'
,它将终止下一次调用
getline
(而不是read ,而是" Ricky"
read
""
)。这会导致输入变得不同步,这很快就会导致您inps >> data
在流位于"Rahul"
. 尝试int
在输入"Rahul"
失败时读取一个,并且失败是粘性的;它会一直存在,直到您重置它,并且所有进一步的尝试都是无操作的。既然你已经读过一些东西line
一次,它永远不会变空,你会永远循环,什么都不做。
第一个也是最重要的变化是在每次
输入后检查输入是否成功,如果没有,不要尝试进一步阅读。(文件的结构是这样的,如果出现错误,您可能无法可靠地重新同步。否则,尝试重新同步并继续是一个很好的策略,这样您就可以在输入中捕获多个错误。)
您需要做的第二件事是确保'\n'
在输入整数时读取完整的行(包括 )。有两种方法:经典的方法是使用
,然后用行getline
初始化 an ,然后使用 this 输入。(这允许额外的错误检查,例如该行中没有额外的垃圾。)或者,您可以调用,它将提取并忽略字符直到(也被提取)。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 行,并解析这些:
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
文件将失败,如上所述,一旦流失败,所有进一步尝试使用它都是无操作的。