简体   繁体   中英

Using getline to read from text file leads to infinite loop when check is eof

I am trying to get some data from a text file that are in specific lines (1st, 7th, 13th, etc - data needed is placed at the next 6th-line)

My code so far is this:

txtfile = "titles.txt";
ifstream txt(txtfile);
const int buffer_size = 80;
char title_buffer[buffer_size];
const int titleLineDiff = 6;
if (txt.is_open())
{
    while(!txt.eof())
    {
        static int counter = 1;
        txt.getline(title_buffer, buffer_size);
        cout << "Title: \"" << counter << "." << title_buffer << "\"" << endl;
        counter++;
        //seek to the next title...difference is 6 lines
        for(int i = 0; i < titleLineDiff; i++)
            txt.getline(title_buffer, 40);
    }
}

Now, it works fine with this file I've created:

testONE
two
three
four 
five
six

testTWO
bla

And it prints "testONE" and "testTWO" but when I am trying to open the file that contains the data, I get an infinite loop and the output is

Title: "counter_increasing_number."

The text document, was copied from the internet and this might be the cause of the problem in reading it.

What can I do about this?


I've changed the code to this:

while(getline(txt,title_buffer))
{
    static int counter = 1;
    //getline(title_buffer, buffer_size);
    cout << "Title: \"" << counter << "." << title_buffer << "\"" << endl;
    counter++;
    //seek to the next title...difference is 6 lines
    for(int i = 0; i < titleLineDiff; i++)
    {
        getline(txt, title_buffer);
    }
}

and it worked.

Could somebody explain me the reason why the first didn't work?

For starters, you're using the results of getline without checking whether is succeeded. (In the second version, this is only true for the inner loop.)

As to why the first version is wrong: whether eof gets set after the last successful read, or after the first unsuccessful, isn't really specified. And of course, there are other reasons input can fail, other than end of file. The consacrated idiom is to always use getline (and any other input) as the control expression in a loop or an if. If the expression is considered true , the input has succeeded. It is sometimes useful to check for eof() after you know the input has failed; if eof() isn't true, then the problem was somewhere else: either a hardware error ( bad() is true) or an error in the format.

As to why your test data and the actual data behave differently, it's hard to say without seeing both. Some possible reasons: different line ending conventions, perhaps one set of data ends with an incomplete line (often the case if data has been generated with a Windows editor), or lines longer than your buffer (in your first case).

Here you go (don't forget to read the comments):

Example:

void Example( void )
{
    // DECLARATION
    // *Declare iFile as std::ifstream and attempt to open file: Example.txt
    std::ifstream iFile( "Example.txt" );

    // *If iFile is open, do this:
    if( iFile.is_open( ) )
    {
        // DECLARATION
        // *You could declare strLine as an array of char if you want
        std::string strLine = "";
        unsigned int nLineCount = 0;

        // DO WHATEVER
        // *Read iFile line by line using std::getline
        while( std::getline( iFile, strLine ) )
        {
            // *For the line after every 6th line, we shall print
            // as a title
            // *( nLineCount % 6 ) gives us the remainder of
            // nLineCount / 6 and if the remainder is 0, then
            // do this:
            if( !( nLineCount % 6 ) )
            {
                std::cout << "Title = " << strLine << std::endl;
            }
            // *For every other line, we shall print it normally
            else
            {
                std::cout << strLine << std::endl;
            }

            // *Increase nLineCount by 1;
            nLineCount ++;
        }
        // CLEAN-UP
        // *Done using inFile - so close it
        inFile.close( );
    }
};

Tested:

Title = 1
2
3
4
5
6
Title = 7
8
9
10
11
12
Title = 13
...

Debugging > Paused! Enter any key to continue...

More... Without comments:

void Example( void ) {
    std::ifstream iFile( "Example.txt" );

    if( iFile.is_open( ) ) {
        std::string strLine = "";
        unsigned int nLineCount = 0;

        while( std::getline( iFile, strLine ) ) {
            if( !( nLineCount % 6 ) )
                std::cout << "Title = " << strLine << std::endl;
            else
                std::cout << strLine << std::endl;
            nLineCount ++;
        }
        iFile.close( );
    }
};

For-loop method, short and clean:

void Example( void )
{
    std::ifstream iFile( "Example.txt" );

    if( iFile.is_open( ) )
    {
        std::string strLine = "";

        for( unsigned int nLineCount = 0; std::getline( iFile, strLine ); nLineCount ++ ) {
            if( !( nLineCount % 6 ) )
                std::cout << "Title = " << strLine << std::endl;
            else
                std::cout << strLine << std::endl;
        }
        iFile.close( );
    }
};

The second file has unequaled lines count as you are expecting 7 lines and it had for the last chunk less than that. this caused you to go beyond the eof before checking it. You need to put eof condition in the internal for loop.

Edit: Please make sure each line is no longer than 80 characters then.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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