# Question about getline with fstream

jtbell
Mentor
I found if I try to read a file into string, only line13 getline(dataF, Crstr) works. line 14 dataF.getline(Crstr,size) flag error no matter how I tried.
The "standalone" getline() function that you used in line 13, reads into a std::string. The getline() function that is a member of the fstream class, that you used in line 14, reads into a C-string. They're two different functions, with the same purpose, but with different types of arguments.

I don't know why the people who wrote the C++ standard that introduced std::string didn't simply add an overloaded version of the fstream getline() member function that accepts a std::string.

C++ has occasional little quirks like that. Another example that used to really irritate me (fortunately no longer valid if you use a fairly recent version of C++):

When I taught C++, you had to use a C-string for the file name, when opening a file to create an fstream. If the file name was in a std::string, you had to use the c_str member function to convert it to a C-string:

Code:
ifstream myStream;
string myFile;
// put something into myFile, then...
myStream.open (myFile.c_str());
Beginning with C++11, you can simply do:
Code:
ifstream myStream;
string myFile;
// put something into myFile, then...
myStream.open (myFile);
Hurrah!

Last edited:
Staff Emeritus
2019 Award
This
It is only when we attempt to read from the file again that a read error occurs. When the call to people.read() fails, the output statement displays the previous value.
and this

you really should test that the read was successful in your code before going on.
I deleted that part when I post to shorten the program here, I always have that, It never have problem in this program.
are mutually inconsistent.

You can't have written code that both tests for read errors and doesn't find a read error.

This

and this

are mutually inconsistent.

You can't have written code that both tests for read errors and doesn't find a read error.
I since added the if(!people){ cout << " Failed"; return 0;} it's fine, still have the same problem.
C++:
//12.15A read structure from file created by 12.15
#include <iostream>
#include <fstream>
#include <string>

using namespace std;

const int name_L = 51, addr_L = 51, phone_L = 14;
struct Info
{
char name[name_L];
//int age;
//char phone[phone_L];
};

int main()
{
int index = 0;
Info person;
char again;
fstream people;

people.open("people.dat", ios::out | ios::binary);
if (!people)
{
cout << " Fail to open people";
return 0;
}
do
{
cout << " Enter name: ";
cin.getline(person.name, name_L);
/*    cout << " Age: "; cin >> person.age;
cin.ignore();
cout << " Enter address line 1: ";
cout << " Enter address line 2: ";
cout << " Enter phone number: ";
cin.getline(person.phone, phone_L);*/
people.write(reinterpret_cast<char*>(&person), sizeof(person));
cout << " Do you want to enter another person? "; cin >> again;
cin.ignore();
} while (tolower(again) == 'y');
people.seekp(0, ios::end);
cout << " sizeof Info = " << sizeof(Info) << "       people.tellp() = " << people.tellp() << "\n\n";
people.close();

people.open("people.dat", ios::in | ios::binary);
if (!people)
{
cout << " Fail to open people";
return 0;
}
do
{
cout << " Name: " << person.name << "\n";
/*    cout << " Age: " << person.age << "\n";
cout << " Enter address line 1: " << person.address1 << "\n\n";
cout << " Enter address line 2: " << person.address2 << "\n\n";
cout << " Enter phone number: "<< person.phone << "\n\n";*/
index++;
cout << " index = " << index << "    !people.eof() = " << !people.eof() << "\n\n";
} while (!people.eof());
people.close();
}
The double read persists even though it pass the if(!people) fail test. That is NOT the issue. I did this many times before I remove the few lines. Read the result, it shows eof() is not true until reading the paul the second time.

pbuk
Gold Member
But if you look at post 18, I actually copied the cmd window that I display !people.eof() and show it did not change to False until reading the Michael the second time. I don't think that's caused by error in reading.
No, it doesn't read "Michael" a second time, it prints it a second time because you don't test whether people.read() has succeeded before you go ahead and print the last value that you read into person. The reason people.read() fails on the 5th pass through the loop is because it attempts to read beyond the end of the file (and therefore the subsequent call to people.eof() returns true).

Oh, you've changed your test data. No, it doesn't read "Paul" a second time, it prints it a second time because you don't test whether people.read() has succeeded before you go ahead and print the last value that you read into person. The reason people.read() fails on the 4th pass through the loop is because it attempts to read beyond the end of the file (and therefore the subsequent call to people.eof() returns true).

No, it doesn't read "Michael" a second time, it prints it a second time because you don't test whether people.read() has succeeded before you go ahead and print the last value that you read into person. The reason people.read() fails on the 5th pass through the loop is because it attempts to read beyond the end of the file (and therefore the subsequent call to people.eof() returns true).

Oh, you've changed your test data. No, it doesn't read "Paul" a second time, it prints it a second time because you don't test whether people.read() has succeeded before you go ahead and print the last value that you read into person. The reason people.read() fails on the 4th pass through the loop is because it attempts to read beyond the end of the file (and therefore the subsequent call to people.eof() returns true).
You are right, I put if(!people) to exit and it only print michael once.
C++:
    do
{
if (!people)
{
cout << " Fail to open people";
return 0;
}
cout << " Name: " << person.name << "\n";
/*    cout << " Age: " << person.age << "\n";
cout << " Enter address line 1: " << person.address1 << "\n\n";
cout << " Enter address line 2: " << person.address2 << "\n\n";
cout << " Enter phone number: "<< person.phone << "\n\n";*/
index++;
cout << " index = " << index << "    !people.eof() = " << !people.eof() << "\n\n";
} while (!people.eof());
BUT it's strange, if I replace if(!people.eof()), it failed the first time without printing anything!!

I thought we should test for eof(), not fail. How do you know that?

I did not expect this can be a confusing issue, don't you read until eof()? In the example in the book, it only test if(!people) right after you open the file to make sure it open without problem. I thought from that onward, you only worry about eof().

Thanks

Last edited:
pbuk
Gold Member
BUT it's strange, if I replace if(!people.eof()), it failed the first time without printing anything!!
I don't find that strange: the ! is doing exactly what I would expect. Do you think it should be there?

I thought we should test for eof(), not fail. How do you know that?
Well testing for eof would work in this case, but there are other reasons a read can fail so you should always check that a read has worked before you assume that it has and press on regardless.

It is also good practice not to return out of a loop like this, instead of return use break and after the loop you can then test to see whether the last record in the file was read successfully or not.

There is no error, it's the expected behavior. eof is set not when you've reached the end, but when you've tried to read passed the end. They should have named it peof (passed end of file).

FactChecker, yungman and jtbell
I think I understand why now. With testing eof(), it has to reach '\0' before it say it's eof.

I was playing with testing the length of file and I got one more than expected, I kind of suspect it's the eof.
C++:
#include <iostream>
#include <fstream>
#include <string>
using namespace std;

int main()
{
char alp[] = "abcdefghijklmnopqrstuvwxyz";//contain 26 characters
fstream alphabet;
alphabet.open("alp.dat", ios::out | ios::binary);
if (!alphabet)
alphabet.write(alp, sizeof(alp));
alphabet.close();

alphabet.open("alp.dat", ios::in | ios::binary);
alphabet.seekg(0, ios::end);
cout << " Length of string = " << alphabet.tellg() << "\n\n";//read 27bytes

return 0;
}
It reported back the length of the string is 27bytes instead of expected 26.

Is this the same idea as my problem reading michael twice because after read the michael the first time, it's not the end of file yet, I have to read one more time to get it to eof()?

Thanks

I think I understand why now. With testing eof(), it has to reach '\0' before it say it's eof.

I was playing with testing the length of file and I got one more than expected, I kind of suspect it's the eof.
C++:
#include <iostream>
#include <fstream>
#include <string>
using namespace std;

int main()
{
char alp[] = "abcdefghijklmnopqrstuvwxyz";//contain 26 characters
fstream alphabet;
alphabet.open("alp.dat", ios::out | ios::binary);
if (!alphabet)
alphabet.write(alp, sizeof(alp));
alphabet.close();

alphabet.open("alp.dat", ios::in | ios::binary);
alphabet.seekg(0, ios::end);
cout << " Length of string = " << alphabet.tellg() << "\n\n";//read 27bytes

return 0;
}
It reported back the length of the string is 27bytes instead of expected 26.

Is this the same idea as my problem reading michael twice because after read the michael the first time, it's not the end of file yet, I have to read one more time to get it to eof()?

Thanks
No, after reading Michael it is the end of the file, but eof doesn't mean end of the file, it means beyond the end of the file (or attempted to go beyond the end of the file).

Like Mark said, it is an indicator why the read failed. If it fails because you tried reading too much/far, then eof is set so that you know the reason. It means that the read failed because you've tried to read beyond the end of the file.

And there is no terminating character for a struct.

Last edited:
Mark44
Mentor
I think I understand why now. With testing eof(), it has to reach '\0' before it say it's eof.
No, the null character has nothing to with the end of a file. The null character, '\0', is used to mark the byte just beyond the end of a C-string. Furthermore, there is no special character that marks the end of the file - the OS knows how many bytes are in a file, so it "knows" when your program is attempting to read beyond the last byte in a file -- that's when eof() returns true.

FactChecker
Gold Member
I don't know why the people who wrote the C++ standard that introduced std::string didn't simply add an overloaded version of the fstream getline() member function that accepts a std::string.
Perhaps they were trying to block people from using the C string functions that allow all the viruses that go beyond the read buffer length.

Thanks guys, I did not know a simple eof() can be this confusing. I have to go back and read exactly what eof() means after dinner and all.

Thanks

FactChecker
Gold Member
Thanks guys, I did not know a simple eof() can be this confusing. I have to go back and read exactly what eof() means after dinner and all.

Thanks
It's really very simple. It is triggered by the error of trying to read data beyond the end of the valid data in the file. The valid data in the file includes any '\0' there indicating the end of a string.

pbuk
Gold Member
Is this the same idea as my problem reading michael twice because after read the michael the first time, it's not the end of file yet, I have to read one more time to get it to eof()?
No, it doesn't read "Michael" a second time, it prints it a second time because you don't test whether people.read() has succeeded before you go ahead and print the last value that you read into person. The reason people.read() fails on the 5th pass through the loop is because it attempts to read beyond the end of the file (and therefore the subsequent call to people.eof() returns true).
You will not understand what is happening until you realise that "Michael" is only being read once.

OK, I just went back and read the book, I even have an exercise program to test eof() RIGHT after reading the last piece of data.
C++:
#include <iostream>
#include <fstream>
using namespace std;
int main()
{//Create a file and write int 10 in it, only one integer data.
int num = 10;
fstream testFile;
testFile.open("demofile.txt", ios::out);
cout << " Writing 10 " << num << " to file.\n";
testFile << num;
cout << " testFile.eof() = " << testFile.eof() << "\n\n";
testFile.close();// close file
//Read from file that has only one integer data.
cout << " Read file. \n";
testFile >> num; //read file to num
cout << " The value   " << num << "   was read.\n\n";
cout << " testFile.eof() = " << testFile.eof() << "\n\n";
return 0;
}
Like before, I got rid of if(!testFile.fail()). It's NOT FAILING so I can make it as short as possible to post here.
You run this program, you don't have to do anything. The program
1) Create the file and write ONLY one integer 10 into the file.
2) Close the file.
3)Open that file.
4)Read the only value of integer 10.
5) Read the eof() bit Right after reading. The bit show testFile.eof() = 1;

This is just like in my program
C++:
    do
{
} while (!people.eof());
I went through the book, it DOES NOT explain eof(). But it makes sense that you read the byte where the internal pointer is pointing. If it is the last byte, it read out the last byte. Then the pointer ADVANCE to the next byte which is '\0'. When the pointer points to '\0', the eof bit will change to 1 right away.

FactChecker
FactChecker
Gold Member
If you look at the file demofile.txt is the number in it?

If you look at the file demofile.txt is the number in it?
Yes. one single number 10.

FactChecker
Gold Member
I stand corrected. The eof()=1 is not an error from trying to read too much from the file. It is just an indicator that there is no more data to be read from the file. If you try to read beyond the end-of-file, both the eof and fail flags will be set, but if you only read to the end-of-file only the eof flag is set. (See http://www.cplusplus.com/reference/ios/ios/eof/ )

I stand corrected. The eof()=1 is not an error from trying to read too much from the file. It is just an indicator that there is no more data to be read from the file. If you try to read beyond the end-of-file, both the eof and fail flags will be set, but if you only read to the end-of-file only the eof flag is set. (See http://www.cplusplus.com/reference/ios/ios/eof/ )
Thanks, I read the program in the link, seems it's like what the eof checking program in post 40.....That after reading the last byte, the pointer advances and pointing to '\0', thereby the eof bit is set right away as shown in post 40. this is the copy of program from cplusplus you linked:
C++:
// ios::eof example
#include <iostream>     // std::cout
#include <fstream>      // std::ifstream

int main () {

std::ifstream is("example.txt");   // open file

char c;
while (is.get(c))                  // loop getting single characters
std::cout << c;

if (is.eof())                      // check for EOF
std::cout << "[EoF reached]\n";
else

is.close();                        // close file

return 0;
}
Line 14 implies you should see eof() after the last read right away.

That's still doesn't explain why I read michael twice before the eof bit is set. You can see it as I cout << !people.eof() and show the bit was NOT set after reading michael the first time, only set after reading the second time.

I went through the book, it does not explain the mechanism, so I am just guessing. In the cplusplus link you gave, it said:

This flag is set by all standard input operations when the End-of-File is reached in the sequence associated with the stream.

Note that the value returned by this function depends on the last operation performed on the stream (and not on the next).

Sounds like the eof is set right after reading the last byte of data.

C++:
#include <iostream>
#include <fstream>

int main () {

std::ifstream is("example.txt");

char c;
while (is.get(c))     //   When does this loop condition become false?
std::cout << c;

if (is.eof())              // eof is reached after the loop condition became false
std::cout << "[EoF reached]\n";
else

is.close();
return 0;
}

Notes

This function only reports the stream state as set by the most recent I/O operation; it does not examine the associated data source. For example, if the most recent I/O was a get() which returned the last byte of a file, eof() returns false. The next get() fails to read anything and sets the eofbit. Only then does eof() return true.

In typical usage, input stream processing stops on any error. eof() and fail() can then be used to distinguish between different error conditions.
https://en.cppreference.com/w/cpp/io/basic_ios/eof

eof is apparently kind of complicated (depends on the IO operation) and not explained well.

At least for get and read (and other "unformated" io operations), it is set after trying to read past the end of the file, but for "formatted" IO operations (which includes <<) it's more complicated.

Last edited:
yungman and FactChecker
Mark44
Mentor
Thanks, I read the program in the link, seems it's like what the eof checking program in post 40.....That after reading the last byte, the pointer advances and pointing to '\0', thereby the eof bit is set right away as shown in post 40.
The program you're referring to is not working with C-strings. As I already said, the null character, '\0', is used to indicate the end of a string. The null character is NOT used to mark the end of a file.
Post #39
You will not understand what is happening until you realise that "Michael" is only being read once.
Post #44
That's still doesn't explain why I read michael twice before the eof bit is set.
That name was NOT read twice!

This is just like in my program
C++:
    do
{
} while (!people.eof());
No, the two programs are not alike. In the first (post #28, I think), you opened the file in binary mode, and used read() to extract the data. The more recent program, in post #40, works with a text file, and uses the stream extraction operator, >>, to get the data.

If I modify your latest program to work like the earlier program, I get different behavior regarding eof().
C++:
#include <iostream>
#include <fstream>
using namespace std;
int main()
{   //Create a file and write an int value into it.
int num = 10;
int val;
fstream testFile;

testFile.open("demofile.txt", ios::out|ios::binary);  // Open for writing in binary mode
cout << " Writing " << num << " to file.\n";
//testFile << str;            // From earlier program
testFile.write((char*)&num, sizeof(num));
cout << " testFile.eof() = " << testFile.eof() << "\n\n";
testFile.close();// close file

testFile.open("demofile.txt", ios::in|ios::binary);    //Open for reading in binary mode
cout << " Read file. \n";
//testFile >> str2; // From earlier program
cout << " The value   " << val << "   was read.\n\n";
cout << " testFile.eof() = " << testFile.eof() << "\n\n";
testFile.close();
}
Output:
Code:
 Writing 10 to file.
testFile.eof() = 0

testFile.eof() = 0
Note that after reading the value in the file, testFile.eof() is false. Obviously reading a file using >> is different from doing so with read(). With read(), you indicate how many bytes you want to read. If it doesn't run out of bytes to read, then eof() is false.

Last edited:
pbuk
C++:
#include <iostream>
#include <fstream>

int main () {

std::ifstream is("example.txt");

char c;
while (is.get(c))     //   When does this loop condition become false?
std::cout << c;

if (is.eof())              // eof is reached after the loop condition became false
std::cout << "[EoF reached]\n";
else

is.close();
return 0;
}

https://en.cppreference.com/w/cpp/io/basic_ios/eof

eof is apparently kind of complicated (depends on the IO operation) and not explained well.

At least for get and read (and other "unformated" io operations), it is set after trying to read past the end of the file, but for "formatted" IO operations (which includes <<) it's more complicated.
Thanks, this is very helpful. The book just doesn't say anything about this. You would think for files, eof() means the same thing. I've been doing more experiments:
C++:
#include <iostream>
#include <fstream>
#include <string>
using namespace std;

int main()
{
char name[100];
fstream people;
people.open("people.dat", ios::out | ios::binary);
people << "alan ";
people << "Paul ";
people << "john ";
people << "michael";
people.close();

people.open("people.dat", ios::in | ios::binary);
do
{
people >> name;
cout << " Name: " << name << "   !people.eof() = " << !people.eof() << "\n\n";
} while (!people.eof());
people.close();
}
I eliminated the writing to struct all together and just write the 4 names into the file, then I just read back.

First, I have to put a space after each name to separate them or else they will be alanPauljohnmichael upon the first read and !people.eof()=0;

I have to put a space after each name, but then you can see !people.eof()=1 after reading michael. The last read is blank, I believe it read the white space I put in after michael. I eliminate the space after michael, it will do everything correctly, only print michael once and exit the program.

I believe in the original program, michael is being read twice. It just using struct, there must be something that cause the eof() to stay as 0 after reading michael the first time.

I change to run the program in TEXT file instead of binary file. RESULT IS EXACTLY THE SAME. There is no difference between text and binary file.

The program you're referring to is not working with C-strings. As I already said, the null character, '\0', is used to indicate the end of a string. The null character is NOT used to mark the end of a file.
Post #39
Post #44
That name was NOT read twice!

No, the two programs are not alike. In the first (post #28, I think), you opened the file in binary mode, and used read() to extract the data. The more recent program, in post #40, works with a text file, and uses the stream extraction operator, >>, to get the data.

If I modify your latest program to work like the earlier program, I get different behavior regarding eof().
C++:
#include <iostream>
#include <fstream>
using namespace std;
int main()
{   //Create a file and write an int value into it.
int num = 10;
int val;
fstream testFile;

testFile.open("demofile.txt", ios::out|ios::binary);  // Open for writing in binary mode
cout << " Writing " << num << " to file.\n";
//testFile << str;            // From earlier program
testFile.write((char*)&num, sizeof(num));
cout << " testFile.eof() = " << testFile.eof() << "\n\n";
testFile.close();// close file

testFile.open("demofile.txt", ios::in|ios::binary);    //Open for reading in binary mode
cout << " Read file. \n";
//testFile >> str2; // From earlier program
cout << " The value   " << val << "   was read.\n\n";
cout << " testFile.eof() = " << testFile.eof() << "\n\n";
testFile.close();
}
Output:
Code:
 Writing 10 to file.
testFile.eof() = 0

testFile.eof() = 0
Note that after reading the value in the file, testFile.eof() is false. Obviously reading a file using >> is different from doing so with read(). With read(), you indicate how many bytes you want to read. If it doesn't run out of bytes to read, then eof() is false.
I KNOW what you said before, I am not convinced, see the post I reply to Jarvis. I don't think the book will ignore this if that is true. I think some else is going on.

The program you're referring to is not working with C-strings. As I already said, the null character, '\0', is used to indicate the end of a string. The null character is NOT used to mark the end of a file.
Post #39
Post #44
That name was NOT read twice!

No, the two programs are not alike. In the first (post #28, I think), you opened the file in binary mode, and used read() to extract the data. The more recent program, in post #40, works with a text file, and uses the stream extraction operator, >>, to get the data.

If I modify your latest program to work like the earlier program, I get different behavior regarding eof().
C++:
#include <iostream>
#include <fstream>
using namespace std;
int main()
{   //Create a file and write an int value into it.
int num = 10;
int val;
fstream testFile;

testFile.open("demofile.txt", ios::out|ios::binary);  // Open for writing in binary mode
cout << " Writing " << num << " to file.\n";
//testFile << str;            // From earlier program
testFile.write((char*)&num, sizeof(num));
cout << " testFile.eof() = " << testFile.eof() << "\n\n";
testFile.close();// close file

testFile.open("demofile.txt", ios::in|ios::binary);    //Open for reading in binary mode
cout << " Read file. \n";
//testFile >> str2; // From earlier program
cout << " The value   " << val << "   was read.\n\n";
cout << " testFile.eof() = " << testFile.eof() << "\n\n";
testFile.close();
}
Output:
Code:
 Writing 10 to file.
testFile.eof() = 0

testFile.eof() = 0
Note that after reading the value in the file, testFile.eof() is false. Obviously reading a file using >> is different from doing so with read(). With read(), you indicate how many bytes you want to read. If it doesn't run out of bytes to read, then eof() is false.

I think it's time for me to move on. Someone gave me a link that really solve the problem, I cannot find that post anymore. Basically it do the read first in line 2 before enter the do-while loop and it works.
C++:
    people.open("people.dat", ios::in | ios::binary);
do
{
cout << " Name: " << person.name << "\n";
cout << " Age: " << person.age << "\n";
cout << " Enter address line 1: " << person.address1 << "\n\n";
cout << " Enter address line 2: " << person.address2 << "\n\n";
cout << " Enter phone number: "<< person.phone << "\n\n";
} while (!people.eof());