Search code examples
c++fstreamistringstream

Infinite loop with file handling and istringstreams


I have a file containing data in the following form:

ballcolor 10
bgcolor 5
[...]

Which I'm trying to read via the following:

void read (const char *filename)
{
    ifstream prefsfile (filename);
    if (prefsfile.is_open ())
    {
        char prefsline [BUFSIZE], prefname [BUFSIZE];
        unsigned int value;

        while (! prefsfile.eof ())
        {
            prefsfile.getline (prefsline, BUFSIZE);

            istringstream iss (prefsline);
            iss >> prefname >> value;
            if (! (iss.fail () || prefsfile.fail ()))
            {
                if (! strcmpi (prefname, PREFSTR_PAD_COLOR) && value <= BACKGROUND_MAX)
                {
                    color.pad = value << 4;
                }
                else if (! strcmpi (prefname, PREFSTR_BALL_COLOR) && value <= FOREGROUND_MAX)
                {
                    color.ball = value;
                }
                else if (! strcmpi (prefname, PREFSTR_FOREGROUND_COLOR) && value <= FOREGROUND_MAX)
                {
                    color.foreground = value;
                }
                else if (! strcmpi (prefname, PREFSTR_BACKGROUND_COLOR) && value <= BACKGROUND_MAX)
                {
                    color.background = value << 4;
                    color.ball |= color.background;
                }
            }
        }
        prefsfile.close ();
    }
}

If I give it a file that exactly conforms to the format shown above, it works well. However, if the file doesn't conform, or if the length of each line is larger than BUFSIZE(=30), it goes in an infinite loop.

How do I resolve the problem?


Solution

  • You are checking for ! prefsfile.eof () but if you checked for the more generic prefsfile.good() your loop should break if the line is longer than BUFSIZE.

    The getline reference explains when the failbit is set. In particular, if the delimiter is not found by the time BUFSIZE characters have been read from the input.

    I would read the file like this:

    void read (const char *filename)
    {
        ifstream prefsfile (filename);
        if (prefsfile.is_open ())
        {
            char prefsline [BUFSIZE], prefname [BUFSIZE];
            unsigned int value;
    
            while (prefsfile.getline(prefsline, BUFSIZE))
            {                        
                if (prefsfile.eof()) {
                    break;
                }
    
                istringstream iss (prefsline);
                iss >> prefname >> value;
                cout << prefname << " " << value << "\n";
            }
            prefsfile.close ();
        }
    }
    

    If the line is too long, the while loop condition will evaluate to false.

    Test 1 - long line breaks the loop

    ballcolor 10
    really really long line that is over 30 characters
    bgcolor 5
    

    output:

    ballcolor 10
    

    Test 2 - shorter lines

    ballcolor 10
    bgcolor 5
    

    output:

    ballcolor 10
    bgcolor 5