1

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?

Chris
  • 3,219
  • 8
  • 39
  • 61

3 Answers3

2

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).

James Kanze
  • 142,482
  • 15
  • 169
  • 310
  • Well it's definitely specified when `eof` gets set. It gets set when extraction hits the end-of-file (§27.7.2.1/3). So it's whenever you do a read that stops at the end-of-file. The main problem with using `.eof()` as the condition is that just because you've not hit the end-of-file doesn't mean your extraction will be successful. The common case of this is the extra `\n` at the end of text files causing the last `std::getline` to fail. – Joseph Mansfield Feb 26 '13 at 23:54
  • @sftrabbit `eof()` is first and foremonst an internal condition; it's set when `rdbuf()->sgetc()` returns `EOF`, and traditionally, the `istream` should make no more calls to the `streambuf`, so it has to memorize the fact. But depending on the type being read, the `istream` may have to look ahead; if it sees `eof` when looking ahead, `eof()` will be set even though the read succeeds. On the other hand, if `istream` doesn't have to look ahead, `eof()` may be false even though there are no more characters to be read. – James Kanze Feb 27 '13 at 08:43
2

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( );
    }
};
CLearner
  • 522
  • 1
  • 4
  • 16
1

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.

Mahmoud Fayez
  • 3,278
  • 2
  • 17
  • 33
  • I changed the for loop to this: `int i = 0; while(i < titleLineDiff && (!txt.eof())){ txt.getline(title_buffer, 80); i++; }` and it didn't work :/ – Chris Feb 26 '13 at 23:42