Question about fstream class and line break

Here is my simple code.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  int main()
{
	fstream fs("config.txt", ios::in);

	string s;
	while (!fs.eof() && fs.good())
	{
		fs >> s;
		cout << "s is " <<s <<endl;
	}


	return 0;
}


In my Windows 10, I created config.txt and wrote:
1
2
3
1CR LF
2CR LF
3CR LF

and the out was within my expect
1
2
3
4
s is 1
s is 2
s is 3
s is 4


But when I compiled code in Linux, and created config.txt:
1
2
3
1 LF
2 LF
3 LF

the outout was:
1
2
3
4
5
s is 1
s is 2
s is 3
s is 4
s is 4


Why did it print "s is 4" twice? I think it may relate to the style that fstream handles LF, but I cannot figure it out clearly.

Anyone knows why?
First, you say that file contains
1
2
3

but program sees also a 4?

You have just read (and printed) the last "word" from the file. There is still whitespace in the file after the word. Stream is not at EOF yet. No read has failed yet. You can do one more iteration.

Why the Windows version does not seem to do that additional iteration? That is a mystery.


1
2
3
4
5
string s;
while ( fs >> s ) // true, if read is successful
{
  cout << "s is " << s << endl;
}
Last edited on
Hello uplife1980

With the line: while (!fs.eof() && fs.good()) does not work the way that you think it does.

"fs.good()" is always true until the file stream is closed.

Checking for "eof" before you read from the file does not catch when the end of file until it is to late and you process something that was not be read.

My experience has been in the 2011 standards when the stream failed the variables would retain their last value. When I started using the 2014 standards I noticed that when the stream failed numeric variables would be set ot (0)zero and strings to empty.

In your Linux example you show 3 lines for the input file and 5 lines of output. Did you miss a line on the input file?

It would be better to write the while loop as: while (fs >> s). This way when the read fails, say when "eof" is set, the while condition fails and the loop is bypasses.

This if fairly normal for reading a file of unknown length.

Andy
Hello uplife1980

My apologies somehow I got the idea the "fs.good()" was something else, that being "fs.open()". May be it was just early or the lack of coffee.

I realized this when looking more at the while loop.

It seems that teachers and books teach the ".eof()" function, but not how to use it properly.

Your while loop condition will work, but you need to do it this way to make the correct use of it.
1
2
3
4
5
6
7
8
fs >> s;

while (!fs.eof() && fs.good())
{
	cout << "s is " <<s <<endl;

	fs >> s;
}

This way the last line of the loop is a read and if it sets the "eof" bit the condition will catch it at the right time.

Sorry about the confusion earlier.

Andy
or the lack of coffee.


Definitely the lack of strong coffee!! :)

@uplife1980 it is extremely rare that you need to use .eof(). Also .good() or .bad()/.fail() are usually only used after a stream extraction to check that the extraction has worked or not (especially if a number (int or double) is read).

In other situations, the stream state is usually checked as part of the stream extraction - often as the condition in a while or for loop. Eg

1
2
3
while (fs >> s) {
    // good extraction - process s
}

Last edited on
The thing to remember about eof() is that it isn't true until you try to read past the end of the file. After you read the last character of the file, eof() is still false. When try to read another character, it becomes true.

Another thing to keep in mind, which they don't seem to teach in schools, is that it's okay to put the loop test in the middle of the loop. That's often the most logical place to put it. So modifying HandyAndy's method (although seeplus's is preferable):
1
2
3
4
5
while true() {
    fs >> s;
    if (fs.eof || !fs.good()) break;
    cout << "s is " << s << '\n';
}
Yeah, but yuk! :)
Sorry guys, it's my fault. I miss the the forth line of "4CR LF" in Windows (or "4LF" in Linux) in this topic.

My point is why in Linux environment, the program would print the last line twice :p
1
2
3
4
{
		fs >> s;
		cout << "s is " <<s <<endl;
	}


If fs >> s fails then the value of s doesn't change and although the stream extrazction didn't work, the cout << statement is still executed and the previous value of s. Hence display the last line twice.

The program would be coded as:

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <fstream>
#include <iostream>

int main()
{
	std::ifstream fs("config.txt");

	if (!fs)
		return (std::cout << "Cannot open input file\n"), 1;

	for (std::string s; fs >> s; )
		std::cout << "s is " << s << '\n';
}

Thank you keskiverto, HandyAndy, dhayden and seeplus , I finally figure out my mistake. Besides, I guess the different results may be a result of different compilers' implement?
Last edited on
Just remember to always test after a file operation, not before.

getline() and >> return the stream after the operation. Hence they can be used as a condition for a for, while, if statement etc.
Topic archived. No new replies allowed.