在读取文件时进入无限循环

时间:2013-08-06 11:56:41

标签: c++ file-io iostream infinite-loop

此代码接受来自输入文件的学生姓名,父亲姓名,掷骰号码和年龄,并以可呈现的方式将其放在输出文件中。

在此代码中,输入文件的内容为:

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();

3 个答案:

答案 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))