简体   繁体   English

读取文件时进入无限循环

[英]Entering in an infinite loop while reading a file

This code accepts student name, father's name, roll no and age from input file and put it in a presentable manner in output file. 此代码从输入文件接受学生姓名,父亲的姓名,卷号和年龄,并将其以可表述的方式放入输出文件中。

In this code, when contents of input file are: 在此代码中,当输入文件的内容为:

Vicky
Mohan
20094567
22   Ricky
Rahul
20091234
21

It works fine. 工作正常。

But if they are: 但是,如果它们是:

Vicky
Mohan
20094567
22
Ricky
Rahul
20091234
21

It enters into an infinite loop. 它进入无限循环。 Any suggestions?? 有什么建议么??

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

It's because of how you read the input. 这是因为您如何阅读输入。 You never actually check if it succeeds or not. 您永远不会真正检查它是否成功。

You need to do eg 你需要做例如

while (std::getline(...))
{
    ...
}

The reason for the symptoms you describe is that you're mixing formatted input with getline . 您描述的症状的原因是您将格式化的输入与getline混合在一起。 There's also a fundamental problem that you never check whether any of the input succeeds. 还有一个根本的问题,就是您从不检查任何输入是否成功。

The real problem manifests itself after the inps >> data lines: these lines skip whitespace and read an int , and no more. 真正的问题在inps >> data行之后显现出来:这些行跳过空白并读取int ,仅此inps >> data In particular, they leave any trailing whitespace, including the '\\n' character, in the stream. 特别是,它们会在流中留下任何尾随空格,包括'\\n'字符。 So in your second case of input, after reading 22 , there is still a '\\n' in the stream, which will terminate the next call to getline (which instead of reading " Ricky" , will read "" ). 因此,在您的第二种输入情况下,在读取22 ,流中仍然有一个'\\n' ,它将终止对getline的下一次调用(而不是读取" Ricky" ,而是读取"" )。 This causes the input to become unsynchronized, which shortly results in your doing inps >> data when the stream is positionned at "Rahul" . 这会导致输入变得不同步,这会在流定位在"Rahul"时很快导致您处理inps >> data Trying to read an int when the input is "Rahul" fails, and failure is sticky; 当输入为"Rahul"时尝试读取int失败,并且失败是粘滞的; it will remain until you reset it, and all further attempts are no-ops. 它会一直保留到您重置它为止,并且所有进一步的尝试都是无操作。 Since you've already read something into line once, it won't ever become empty, and you loop forever, doing nothing. 因为您已经读过line ,所以它永远不会变空,并且您将永远循环,无所事事。

The first, and most important change is to check after every input that the input succeeded, and not try to read further if it hasn't. 第一个也是最重要的更改是在每个输入之后检查输入是否成功,如果没有输入,请不要尝试进一步阅读。 (The structure of your file is such that you probably can't reliably resynchronize if there is an error. Otherwise, it is a good policy to try and resynchronized, and continue, so that you can catch multiple errors in the input.) (文件的结构是这样的,如果出现错误,您可能无法可靠地重新同步。否则,尝试重新同步并继续操作是一个好策略,这样您就可以在输入中捕获多个错误。)

The second thing you need to do is ensure that you read a complete line (including the '\\n' ) when inputting integers. 您需要做的第二件事是确保在输入整数时阅读完整的一行(包括'\\n' )。 There are two ways of doing this: the classic way is to use getline , then initialize an std::istringstream with the line, and input the int using this. 有两种方法:经典方法是使用getline ,然后使用该行初始化std::istringstream ,并使用此方法输入int (This allows additional error checking, eg that there is no additional garbage in the line.) Alternatively, you can call inps.ignore( std::numeric_limits<std::streamsize>::max(), '\\n' ); (这允许进行其他错误检查,例如,该行中没有其他垃圾。)或者,您可以调用inps.ignore( std::numeric_limits<std::streamsize>::max(), '\\n' ); , which will extract and ignore characters until '\\n' (which is also extracted). ,它将提取并忽略字符,直到'\\n' (也将被提取)。

EDIT: 编辑:

On rereading, it occurs to me that my text description isn't all that clear, so here's what happens in a step-wise explination: 重新阅读时,我发现我的文字描述不太清楚,所以这是逐步分解的过程:

  • The first time through the loop, everything works as expected, but the input position is immediately behind the "22" (which was the last input). 第一次通过循环,一切都按预期进行, 但是输入位置紧接在"22" (这是最后一个输入)之后。

  • The getline at the top of the loop is called. 调用循环顶部的getline It will return all of the characters between the "22" and the end of that line. 它将返回"22"和该行末尾之间的所有字符。 If the "22" is immediately followed by a new line, this should result in an empty line, terminating the loop (although there is still more data to be read). 如果"22"后面紧跟着新行,则这将导致空行,从而终止循环(尽管仍有更多数据要读取)。 If there are extra characters after the "22" (say a blank or so), then these will be read as the line. 如果在"22"之后还有多余的字符(例如一个空格左右),那么这些字符将被视为该行。

  • Assuming there were extra characters, you then read " Ricky" as the father's name, and do inps >> data for the roll number on the string "Rahul" . 假设有多余的字符,则将“ Ricky”作为父亲的名字,然后对字符串"Rahul"上的掷骰数进行inps >> data This fails, and sets the stream in an error condition, which causes all further operations to be no-ops. 这将失败,并将流设置为错误状态,这将导致所有其他操作变为无操作。

  • So when you next reach the top of the loop, the getline is a no-op, the previous contents of line are unchanged, and you enter the loop again. 因此,当您下一个到达循环的顶部时, getline是空操作,该line的先前内容未更改,然后再次进入循环。 And again, and again, because until you clear the error, all operations will be no-ops. 一遍又一遍,因为直到您清除错误,所有操作都将变为无操作。 All of the variables hold their old values. 所有变量均保留其旧值。

The simplest solution is probably that suggested by Neil Kirk in a comment: read the entire file into an std::vector of lines, and parse those: 最简单的解决方案可能是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>()) );

If you want to read the file on the fly, however (say because it might be too big to fit into memory, or simply because it is a good learning exercise): 但是,如果您想即时读取文件(例如,因为它可能太大而无法容纳到内存中,或者仅仅是因为这是一个很好的学习练习):

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

This is very succinct. 这非常简洁。 It still needs additional error checking, and you probably want to keep track of the line number so that you can output it with any error message. 它仍然需要进行其他错误检查,您可能希望跟踪行号,以便可以将其与任何错误消息一起输出。 But it shoul point you in the right direction. 但这应该将您指向正确的方向。

EDIT2: 编辑2:

Also, you don't want to open the output file each time through the loop. 另外,您不想每次通过循环都打开输出文件。 Attempting to open an already open std::ofstream will fail, an as above, once the stream has failed, all further attempts to use it are no-ops. 尝试打开一个已经打开的std::ofstream将会失败,如上所述,一旦该流失败,所有进一步使用它的尝试都是无操作的。

Replace 更换

for(getline(inps,line);line!="";getline(inps,line))

with

while (getline(inps, line))

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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