If I had to make a list of top 10 things new programmers do wrong in C or C++, feof/eof would be on that list with certainty. Partly the trouble arises because many learning resources or other people's code is misleading or wrong. And partly because newcomers take a look at the function title (end of file) and jump to what seems to be a logical conclusion. They say: here's the logic I'm coding: (1) test if you're at the end of the file, and (2) if not, go ahead try to read in something.
Nice, but that's not what you coded. Take a very close look at
eof. It never says eof returns true when you're at the end of the file. No, seriously, it doesn't. It says it returns true
when the eofbit flag is set. Here's the big thing most newbies miss: eofbit is only set when there's a failed operation.
So let's say you read a bunch of times and reach the end of the file. How many failed operations have you had so far? None. Are you at the end of the file? Yes. Will eof return true? No, because there is no failed operation due to eof. See the impending doom? You're code assumes that if .eof() returns false, it's safe to go ahead and read from the file and do some operations on the input. But that file I/O operation will fail, and the last known input will be returned. Hence that last line repeating twice effect you see.
Which is why your second attempt that actually works is in reality an ugly, ugly, ugly hack. Your final piece of code comes across the right idea, although it isn't robust because of a mistake.
Everything has a return value. You as a programmer might not have the experience to code functions with meaningful error handling and return values, but the standard libraries most certainly have been designed in such a manner. File I/O, for example, has a return value, which you can use in a while loop, or in fact other test conditions. Worried about reaching the end of the file? Attempt to read, and check the return value. Actually, you should always check the return value regardless of whether you anticipate EOF or not. Anyway, if you do hit EOF, the return value will reflect the failure appropriately.
Your third example isn't quite robust because it assumes that if you can read in a firstfield, there is a number to read in as well. That's relying on well formed input file. Bad idea. A better idea is to use getline to take in a whole line and then parse appropriately with string streams.
As a side note, take a look at the C++ String class. It's preferable to using C-style character array strings, unless you have a good reason. Look at getline that's part of the string class, and also, look at string streams in C++.