繁体   English   中英

读取文件时进入无限循环

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM