Question about getline with fstream

  • Thread starter Thread starter yungman
  • Start date Start date
AI Thread Summary
The discussion focuses on the use of `getline` with `fstream` in C++, highlighting the differences between reading into a string and an array. The user experiences compile errors when using `dataF.getline` with a string, while `getline(dataF, Crstr)` works correctly. Additionally, confusion arises regarding the behavior of `seekg` when reading from a file, particularly how the file pointer advances after reading. Explanations clarify that the pointer moves to the next character after a read operation, and the user learns about the importance of correctly using offsets with `seekg`. Overall, the conversation emphasizes the nuances of file handling in C++ and the need for understanding function specifications.
yungman
Messages
5,741
Reaction score
294
I have been reviewing Chapter 12 files. From experiment,
C++:
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main()
{ 
    char Ar[] = "demofile.txt";
    string Cwstr, Crstr;
    char cAr[100];
    int size = 100;
    fstream dataF;
    dataF.open(Ar, ios::in);
    getline(dataF, Crstr);
    //dataF.getline(Crstr, size);
    cout << Crstr << "\n\n";
    dataF.close();
    return 0;
}

Below, when I said doesn't work, I mean VS gave me a compile error. But if I tell compiler to just run it, it seems to work.

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 opposite is true if I read the file into an array, getline(dataF,Ar) flag error and only dataF.getline(Ar, sizeof(Ar)) works.

The book only talk about dataF.getline with Array, I learn getline(dataF,Crstr) here or online. Is my finding valid?

Thanks
 
Technology news on Phys.org
Filip Larsen said:
I recommend you try look at the specification for the two API methods (e.g. see [1] and [2]) and see if you can explain your own observations.

[1] https://en.cppreference.com/w/cpp/string/basic_string/getline
[2] https://en.cppreference.com/w/cpp/io/basic_istream/getline
My problem is I have not study any of these, like this one, I don't understand any line of these:
Compile error Listing 7.2.jpg


That's the frustration when go online, I am sure if I study a few more months, I should be able to understand this better. I have not study class, template and all that.

Thanks anyway.
 
Forget about the last question, I know how it works within the context of the chapter already. I cannot stop and look into Classes, template and all that. It becomes chicken and eggs. I was hoping to get a yes or no on that only. I have a more pressing problem I cannot figure out:I am working on random files seekg. This is the program in the exercise. For the life of me, I cannot figure how this works:
C++:
//12.17 random file seekg
#include <iostream>
#include <fstream>
using namespace std;

int main()
{
    char ch;
    char Cw[] = "abcdefghijklmnopqrstuvwxyz";
    fstream file;
    file.open("letters.txt", ios::out);
    file << Cw;
    file.close();

    file.open("letters.txt", ios::in);
//Move to byte 5 from beginning(the 6th byte)and read in file and write to ch.
    file.seekg(5L, ios::beg);
    file.get(ch);
    cout << " Byte 5 from beginning is = " << ch << "\n\n";// it's 'f'.
//Move to the 10th byte from the end of the file and read the character there.
    file.seekg(-10L, ios::end);
    file.get(ch);
    cout << " The 10th byte from the end is = " << ch << "\n\n";//it's 'q'.
//move to byte 3 from current position.(the 4th byte) and read the character there
    file.seekg(3L, ios::cur);
    file.get(ch);
    cout << " Byte 3 from current = " << ch << "\n\n";// it's  'u'.
    file.close();
    return 0;
}
The first one is line 17 file.seekg(5L, ios::beg). So starting from byte 0 ('a'), go to byte 5 which is 'f'. That's easy.
Then line 21 file.seekg(-10L, ios::end). Looks like the end is 1byte after 'z', so byte( -1) is 'z'. so byte(-10) is 'q'. Still easy.

Then line 25 file.seekg(3L, ios::cur). The current position is 'q' after line 21. So learning from line 17, using 'q' as byte 0, so byte 3 should be 't'; WRONG, it is 'u'.

I type the program EXACTLY the same as in the book. I even copy the comments from the book telling how to count. for the life of me, I cannot figure out how to count this out.

I change line 25 to file.seekg(0, ios::cur) and ran the program, it show it's 'r'! How the hell it moves to 'r'?
Also when I change line 21 to file.seekg(0, ios::end), it become 'a'! It wrap around? I though it should be eof(0x00).
Please help. I've been stuck over a day already.

Thanks
 
Last edited:
yungman said:
Then line 25 file.seekg(3L, ios::cur). The current position is 'q' after line 21.
Yes the cursor is at 'q' after line 21, but when you do file.get() in line 22 it moves it to 'r'...
yungman said:
So learning from line 17, using 'q' as byte 0, so byte 3 should be 't'; WRONG, it is 'u'.
... and then moving 3 more to the right moves to 'u'. You don't need to specify 3 as a long.

yungman said:
I change line 25 to file.seekg(0, ios::cur) and ran the program, it show it's 'r'! How the hell it moves to 'r'?
file.seekg(0, ios::cur) doesn't move it at all, it was at 'r' after file.get() in line 22.

yungman said:
Also when I change line 21 to file.seekg(0, ios::end), it become 'a'! It wrap around? I though it should be eof(0x00).
I don't think it does wrap around, are you sure you typed ios::end correctly? It looks like it has instead moved the cursor 0 bytes from the beginning (which is the default base position if the second argument is missing).

If you want to know where the cursor is then you can use file.tellg(). Alternatively you can read the value at the cursor position without moving the cursor with file.peek() although this doesn't support reading into a char. Alternatively after get() you can use unget() to move the cursor back - but you should first check that the get() did not fail because of EOF.
 
pbuk said:
Yes the cursor is at 'q' after line 21, but when you do file.get() in line 22 it moves it to 'r'...
@yungman, this and the other comments by pbuk are explanations of why you're seeing the behavior of your program.
Whichever character in a file is the next one to be read or written is determined by the position-in-file pointer, an internal mechanism.
After doing a read operation (i.e., with file.get()) or write operation (with file.put()), the position-in-file pointer is advanced to the next character, the next one to be read or written.
The idea is similar if you read or write multiple characters, with the position-in-file pointer being moved to the appropriate position.
 
  • Like
Likes yungman and pbuk
You wrote this in post #1.
yungman said:
Below, when I said doesn't work, I mean VS gave me a compile error. But if I tell compiler to just run it, it seems to work.
If you get a compile error, the program with the error won't run. In this case, VS will ask you if you want to run the last successful build. So "it seems to work" means that you were running a previous version that didn't have any build errors.
 
  • Like
Likes yungman, Vanadium 50 and pbuk
yungman said:
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 opposite is true if I read the file into an array, getline(dataF,Ar) flag error and only dataF.getline(Ar, sizeof(Ar)) works.

The book only talk about dataF.getline with Array, I learn getline(dataF,Crstr) here or online. Is my finding valid?
getline() and fstream::getline() (which is what is called by dataF.getline()) are completely different functions so there is no reason to expect them to work interchangeably. You will understand this better when you have moved on to classes.
 
Last edited:
  • Like
Likes yungman
pbuk said:
Yes the cursor is at 'q' after line 21, but when you do file.get() in line 22 it moves it to 'r'...

... and then moving 3 more to the right moves to 'u'. You don't need to specify 3 as a long.file.seekg(0, ios::cur) doesn't move it at all, it was at 'r' after file.get() in line 22.I don't think it does wrap around, are you sure you typed ios::end correctly? It looks like it has instead moved the cursor 0 bytes from the beginning (which is the default base position if the second argument is missing).

If you want to know where the cursor is then you can use file.tellg(). Alternatively you can read the value at the cursor position without moving the cursor with file.peek() although this doesn't support reading into a char. Alternatively after get() you can use unget() to move the cursor back - but you should first check that the get() did not fail because of EOF.
Thanks so much for the explanation. I totally forgot the most basic thing about file read and write...that right after you read or write, it will point to the NEXT element to be written or read.!

Yes, that's the reason on the first line 17, when I change from 5 to 0, it gave me 'a', BECAUSE there is no file.get(ch) in front of it, so it never advance to the next element.

I don't think I type wrong on file.seekg(0, ios::end). I actually just change -10 to 0 in line 21 and run the program. I did that many times and it came up with 'a' every time. I just did rebuild solution and clean solution and run it again, still gives me 'a'. If I change line 21 to file.seekg(-1, ios::end) it will give me 'z'.

I never understand why they put 10L, -3L with the 'L'. The book actually described as long integer.

Compile error Listing 7.2.jpg


Ha ha, Maybe now it's time for you to say I read it wrong!

Yes, I did try getting rid of the L and the program works the same. At least I know char is 1 byte, the string is only 26byte + eof = 27byte long. there is no way it is counted as long integer of 8 bytes each.

I can't wait to get to tellg and tellp after this.

Thanks so much for taking the time to explain this. Now I can move on.
 
  • #10
Mark44 said:
You wrote this in post #1.If you get a compile error, the program with the error won't run. In this case, VS will ask you if you want to run the last successful build. So "it seems to work" means that you were running a previous version that didn't have any build errors.
I did not know that if I choose yes, it only display the last successful run. I will never do that again.

Thanks so much for helping me.
 
Last edited:
  • #11
yungman said:
Also when I change line 21 to file.seekg(0, ios::end), it become 'a'! It wrap around? I though it should be eof(0x00).
An offset of 0 from the end is the final byte of the file -- eof. To get the last valid character in the file, do this:
C++:
seekg(-1L, ios::end);
Here we have an offset of -1 (toward the beginning) from the end of the file.
BTW, the first argument to seekg() is of type streamoff, which in VS is typedef-ed as either __int64 or long. If you use an int value, it will be promoted to a long.
This wiki article about seekg, https://en.wikipedia.org/wiki/Seekg, has this to say.
Note: If you have previously got an end of file on the stream, seekg will not reset it but will return an error in many implementations. - use the clear() method to clear the end of file bit first.

I modified your program as shown below. The output is shown in the comments.
C++:
    file.seekg(-2L, ios::end);
    file.get(ch);
    cout << " Next to last byte before EOF is = " << ch << "\n\n";//it's 'y'.
    //file.clear();      Not needed, since I didn't attempt to read beyond the last valid byte
    file.seekg(0L, ios::beg);
    file.get(ch);
    cout << " Byte at beginning of file is = " << ch << "\n\n";// it's 'a'.
//move to byte 3 from current position.(the 4th byte) and read the character there
    file.seekg(3L, ios::cur);
    file.get(ch);
    cout << " Byte 3 from current = " << ch << "\n\n";// it's  'e'.
    file.close();
 
  • #12
yungman said:
I did not know that if I choose yet, it only display the last successful run. I will never do that again.

Thanks so much for helping me.
Whether there is something new to run depends on whether you got a compiler error or a compiler warning. It may compile a new program if there are only warnings. When you ask for help, you should always tell us exactly what the error/warning message is. It makes a big difference and there is no reason to make us guess.
 
  • #13
Mark44 said:
An offset of 0 from the end is the final byte of the file -- eof. To get the last valid character in the file, do this:
C++:
seekg(-1L, ios::end);
Here we have an offset of -1 (toward the beginning) from the end of the file.
BTW, the first argument to seekg() is of type streamoff, which in VS is typedef-ed as either __int64 or long. If you use an int value, it will be promoted to a long.
This wiki article about seekg, https://en.wikipedia.org/wiki/Seekg, has this to say.

I modified your program as shown below. The output is shown in the comments.
C++:
    file.seekg(-2L, ios::end);
    file.get(ch);
    cout << " Next to last byte before EOF is = " << ch << "\n\n";//it's 'y'.
    //file.clear();      Not needed, since I didn't attempt to read beyond the last valid byte
    file.seekg(0L, ios::beg);
    file.get(ch);
    cout << " Byte at beginning of file is = " << ch << "\n\n";// it's 'a'.
//move to byte 3 from current position.(the 4th byte) and read the character there
    file.seekg(3L, ios::cur);
    file.get(ch);
    cout << " Byte 3 from current = " << ch << "\n\n";// it's  'e'.
    file.close();
Thanks

I don't quite get what is highlighted above, you mean I can specify what type like long int, int etc.? Can you give me a few examples like what you wrote examples of reinterpret_cast<float*>(&x).

I looked at wiki link, I have not learn istream&....

But I understand and know how to work like in the program you provided. I think I am good on this already. After I found my mistake of not counting of file.get() that advance the pointer, it all make sense.

I know file.seekg(-1, ios::end) is 'z', that's why I know the pointer was pointing to eof. But when I ran the program file.seekg(0, ios::end), it did give me 'a'. I rebuild and clear solution, and still it's 'a'.

Thanks a million. It's very helpful.
 
  • #14
Mark44 said:
An offset of 0 from the end is the final byte of the file -- eof. To get the last valid character in the file, do this:
C++:
seekg(-1L, ios::end);
Here we have an offset of -1 (toward the beginning) from the end of the file.
BTW, the first argument to seekg() is of type streamoff, which in VS is typedef-ed as either __int64 or long. If you use an int value, it will be promoted to a long.
This wiki article about seekg, https://en.wikipedia.org/wiki/Seekg, has this to say.

I modified your program as shown below. The output is shown in the comments.
C++:
    file.seekg(-2L, ios::end);
    file.get(ch);
    cout << " Next to last byte before EOF is = " << ch << "\n\n";//it's 'y'.
    //file.clear();      Not needed, since I didn't attempt to read beyond the last valid byte
    file.seekg(0L, ios::beg);
    file.get(ch);
    cout << " Byte at beginning of file is = " << ch << "\n\n";// it's 'a'.
//move to byte 3 from current position.(the 4th byte) and read the character there
    file.seekg(3L, ios::cur);
    file.get(ch);
    cout << " Byte 3 from current = " << ch << "\n\n";// it's  'e'.
    file.close();
Hi Mark
I just finished writing notes on chapter 12. I actually copy one part from you on reinterpret_cast that show converting to different datatype examples in that section. At this point, I am not trying to understand everything as a lot of them are in later chapters. Instead of try to understand like Casting and Classes at this point, I just write down how to use it and use the notes like a cheat sheet when writing programs. Like I never understand why programs in books always start with int main(){... return0;} until I studied chapter 6 functions, then I understand that main can be a function that can be called also and return an integer. If I kept trying to understand this in chapter 1, I will never get through the chapter. Like I still don't quite understand static_cast on int x=5; that int is datatype, x is an object and all that, all I know it is the same thing like fstream dataF or struct people{..}; that declare a datatype and given the internal members etc. If I get stuck on casting and all that now, I'll never move on. At this point, I just want to know the right way to use it within the context of the current chapter of the Gaddis book.

I have been working on my notes the last two days on the former chapters including page number to make sure I can copy the correct syntax when writing the codes and can refer back to the book if needed. Did find quite a few mistakes looking back.

Thanks
 

Attachments

  • #15
yungman said:
Instead of try to understand like Casting and Classes at this point, I just write down how to use it and use the notes like a cheat sheet when writing programs.
There's nothing really very complicated about casts. C and C++ are strongly typed, which means, for example, that the value being assigned to a variable should be of the same type as the variable, or that the actual arguments in a function call should be of the same type as the parameter types in the function declaration.
yungman said:
Like I never understand why programs in books always start with int main(){... return0;} until I studied chapter 6 functions, then I understand that main can be a function that can be called also and return an integer.
And you don't need to include the final return 0;. Apparently the standards committee decided to no long require it. The main() function defines the entry point for the program. It's called from the command prompt if you run your code outside of an IDE like VS. When the last line in main() executes, control returns back to the operating system running the command prompt window.

I saw a few mistakes in your cheat sheet. There might be a few more that I didn't catch.
yungman said:
*P652: fstream data type: fstream dataF; Object dataFile needs 2 arguments:
dataF.open(“filename”,File Access Flag);
dataF is an object, an instance of the fstream class, but it's the open() function that takes the 2 arguments. When you start studying classes in earnest, it's important to distinguish between a class (such as fstream) and its member functions. A class will typically have one or more member functions with the same name, the constructors for the class. If there are multiple constructors, they will have different sets of parameters. There will also be a destructor with almost the same name, except its name starts off with the ~ character.
yungman said:
*P666:
*Continuous read: Ar[50]; while(dataFile.eof()){ dataFile >> Ar; cout Ar;} Will display data1 data2 data3.
Your logic is wrong here. Unless EOF has already been reached, the body of the while loop won't execute at all. The while header should be
while(!dataFile.eof()) ...
Your version is missing the ! (logical NOT) on the test expression.
yungman said:
*P678:reinterpret_cast<datatype>(value). <datatype> is data type to convert to, value is value you are
converting. In here <datatype> is char pointer.
There is nothing in what you wrote that says that <datatype> is char pointer.
When you use this cast with write(), then the first parameter has to be char*, but that's just one use of this cast, which can be used for other purposes.

yungman said:
*P678: .read and .write function expect the 1st argument be a pointer to a char. Must use reinterpret_cast
when writing and reading items of other data type to binary files.
1) the names of these functons are read() and write(), not .read and not .write.
2) reinterpret_cast is not required. The older style C cast can be used. The following works just fine.
C++:
dataFile1.write((char *)(&x), sizeof(x));

yungman said:
*The following are examples of using reinterpret_cast: int Ar[] = {1, 2, 3, 4, 5};
float *pFloat = reinterpret_cast<short*>(Ar) double *pDouble = reinterpret_cast<double*>(Ar)
Short *pShort = reinterpret_cast<short*>(Ar) char *pChar = reinterpret_cast<char*>(Ar)
Copy/paste error. The first one should be
float *pFloat = reinterpret_cast<float*>(Ar)

It could also be written as
float *pFloat = (float*)Ar ;
Again, casts are much less complicated than you seem to believe. All we're doing here is taking a value (Ar, presumably of type int[], which is more-or-less the same as int*), and telling the compiler to treatit as if it were the address of a float. That's all. There is no change at all to the bit pattern that makes up the address.
 
Last edited:
  • #16
Mark44 said:
There's nothing really very complicated about casts. C and C++ are strongly typed, which means, for example, that the value being assigned to a variable should be of the same type as the variable, or that the actual arguments in a function call should be of the same type as the parameter types in the function declaration.
And you don't need to include the final return 0;. Apparently the standards committee decided to no long require it. The main() function defines the entry point for the program. It's called from the command prompt if you run your code outside of an IDE like VS. When the last line in main() executes, control returns back to the operating system running the command prompt window.

I saw a few mistakes in your cheat sheet. There might be a few more that I didn't catch.
dataF is an object, an instance of the fstream class, but it's the open() function that takes the 2 arguments. When you start studying classes in earnest, it's important to distinguish between a class (such as fstream) and its member functions. A class will typically have one or more member functions with the same name, the constructors for the class. If there are multiple constructors, they will have different sets of parameters. There will also be a destructor with almost the same name, except its name starts off with the ~ character.
Your logic is wrong here. Unless EOF has already been reached, the body of the while loop won't execute at all. The while header should be
while(!dataFile.eof()) ...
Your version is missing the ! (logical NOT) on the test expression.
There is nothing in what you wrote that says that <datatype> is char pointer.
When you use this cast with write(), then the first parameter has to be char*, but that's just one use of this cast, which can be used for other purposes.

1) the names of these functons are read() and write(), not .read and not .write.
2) reinterpret_cast is not required. The older style C cast can be used. The following works just fine.
C++:
dataFile1.write((char *)(&x), sizeof(x));

Copy/paste error. The first one should be
float *pFloat = reinterpret_cast<float*>(Ar)

It could also be written as
float *pFloat = (float*)Ar ;
Again, casts are much less complicated than you seem to believe. All we're doing here is taking a value (Ar, presumably of type int[], which is more-or-less the same as int*), and telling the compiler to treatit as if it were the address of a float. That's all. There is no change at all to the bit pattern that makes up the address.

Thanks you so much to take the time reading it. I definitely print this out and read it very carefully.

Thanks
 
Last edited by a moderator:
  • #17
Hi Mark
I read through your response once, thank you for the information. I see dataF is the object, open(), write() and read() are the functions, not dataF.open() together is a function.

I deleted return 0; it works. But when I deleted int int in int main(), that got me an error. I put void main(), it works as I don't return anything. I guess I have to declare the datatype of the function main().

I am going to try float*pFloat = (float*)Ar tomorrow. Save a lot of typing! I have a bigger fish to fry right now that I don't quite get why I get one more read before eof().Thanks
 
Last edited:
  • #18
Hi
I have one problem that I read the last file twice before the eof() terminate the the do-while(people.eof()) loop.
C++:
#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 address1[addr_L], address2[addr_L];
    //char phone[phone_L];
};

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

    people.open("people.dat", ios::out | ios::binary);
    do
    {
        cout << " Enter name: ";
        cin.getline(person.name, name_L);
    /*    cout << " Age: "; cin >> person.age;
        cin.ignore();
        cout << " Enter address line 1: ";
        cin.getline(person.address1, addr_L);
        cout << " Enter address line 2: ";
        cin.getline(person.address2, addr_L);
        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.close();

    people.open("people.dat", ios::in | ios::binary);
    do
    {
        people.read(reinterpret_cast<char*>(&person), sizeof(person));
        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();
}

I put 3 out of 4 member components of the struct dataF person as comment to save typing. The struct only ask for name right now.
The first part is just writing as many person until I choose to end it. The second part of the program is to just print out all the person in the file. I enter 4 names, but when I read back, I read all 4 and repeat the last one before !people.eof()=0. As shown below:
Compile error Listing 7.2.jpg


You see I input 4 names, but when read back, michael is repeated twice. I print out the !people.eof() and you can see it's only after reading michael again before !people.eof() = 0.

I am sure it's my mistake, I just cannot see it. I put the read() inside the loop, I should get eof() after reading michael the first time.

BTW, other than repeating the last one, the program works with all the rest of the members that I put as comments.

thanks
 
  • #19
yungman said:
I see dataF is the object, open(), write() and read() are the functions
Yes. dataF is an object - an instance of the fstream class. open, write, and read are member functions of that class.
yungman said:
not dataF.open() together is a function.
The dot notation is how you access a member of an object, whether the member is a data member or a function member. The dot notation is also how you access a member of a struct. The line above is calling the open function on the dataF object.
yungman said:
But when I deleted int int in int main(), that got me an error. I put void main(), it works as I don't return anything. I guess I have to declare the datatype of the function main().
Yes, you have to declare a return type for main. VS allows you to write "void main(...)", but some other compilers don't allow this. "int main(...)" is the standard usage.
 
  • Like
Likes yungman
  • #20
Regarding your program in post #18, I think this is what is happening. Assuming there are N names in the file, the program can read the Nth name with no problem. 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.

Here's a loop that works correctly.
C++:
while(true)
    {
        people.read(reinterpret_cast<char*>(&person), sizeof(person));
        bool state = people.rdstate();        
        if (state) break;    // If any of badbit, failbit, eofbit are set, state will be nonzero; i.e., true    
        cout << " Name: " << person.name << "\n";        
        index++;
        cout << " index = " << index << "    !people.eof() = " << !people.eof() << "\n\n";
    }
When the call to people.read(...) fails, people.eofbit and people.failbit are set, so people.rdstate() will return true.
 
  • Like
Likes yungman
  • #21
Mark44 said:
Yes. dataF is an object - an instance of the fstream class. open, write, and read are member functions of that class.
The dot notation is how you access a member of an object, whether the member is a data member or a function member. The dot notation is also how you access a member of a struct. The line above is calling the open function on the dataF object.
Yes, you have to declare a return type for main. VS allows you to write "void main(...)", but some other compilers don't allow this. "int main(...)" is the standard usage.
Thanks

So in these cases here: int x; float y; char z; struct people;

fstream dataF
where int, float, char, struct, fstream and ALL datatype.

x, y, z, people and dataF are ALL Objects.

I still don't quite get where instance comes in. x, y, z are all Object of their respective datatype, then you said they are instance of of their datatype.

Thanks
 
  • #22
Mark44 said:
When the call to people.read() fails, the output statement displays the previous value.

@yungman, you really should test that the read was successful in your code before going on. It is a very bad habit to assume that a read was successful, especially a read of something expected to be in a certain format, and especially especially if the input was from a person.
 
  • #23
Vanadium 50 said:
@yungman, you really should test that the read was successful in your code before going on. It is a very bad habit to assume that a read was successful, especially a read of something expected to be in a certain format, and especially especially if the input was from a person.
I deleted that part when I post to shorten the program here, I always have that, It never have problem in this program.
 
  • #24
Mark44 said:
Regarding your program in post #18, I think this is what is happening. Assuming there are N names in the file, the program can read the Nth name with no problem. 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.

Here's a loop that works correctly.
C++:
while(true)
    {
        people.read(reinterpret_cast<char*>(&person), sizeof(person));
        bool state = people.rdstate();      
        if (state) break;    // If any of badbit, failbit, eofbit are set, state will be nonzero; i.e., true  
        cout << " Name: " << person.name << "\n";      
        index++;
        cout << " index = " << index << "    !people.eof() = " << !people.eof() << "\n\n";
    }
When the call to people.read(...) fails, people.eofbit and people.failbit are set, so people.rdstate() will return true.
Thanks for the reply

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.

I since added line 41 to display the size of the structure sizeof(Info). The sizeof(Info) = 51 bytes. Then I find the total bytes using people.seekp(0, ios::end); people.tellp() = 153 for 3 names. So that proofs I enter the correct number of names in the file and the file stored the correct number of names.
C++:
#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 address1[addr_L], address2[addr_L];
    //char phone[phone_L];
};

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

    people.open("people.dat", ios::out | ios::binary);
    do
    {
        cout << " Enter name: ";
        cin.getline(person.name, name_L);
    /*    cout << " Age: "; cin >> person.age;
        cin.ignore();
        cout << " Enter address line 1: ";
        cin.getline(person.address1, addr_L);
        cout << " Enter address line 2: ";
        cin.getline(person.address2, addr_L);
        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);
    do
    {
        people.read(reinterpret_cast<char*>(&person), sizeof(person));
        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();
}

Here is the copy of cmd window after running:
Compile error Listing 7.3.jpg


too bad there are things I cannot display in debug-->window-->immediate
Compile error Listing 7.2.jpg

I have to write that in the program on eof() and people.tellp(). I am having a ball with this debugger!

I deleted test of file.failure to shorten the program here. It never give me problem.

Thanks
 
  • #25
Vanadium 50 said:
@yungman, you really should test that the read was successful in your code before going on. It is a very bad habit to assume that a read was successful, especially a read of something expected to be in a certain format, and especially especially if the input was from a person.
A good habit is to read the input as an asci string and then scan the saved result to see if the format is correct. You can try scanning different ways with different formats, each time trapping any error, to determine what the user input was. That makes the reads much more robust and predictable and allows you to test the input.
 
  • #26
yungman said:
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! :woot:
 
Last edited:
  • #27
This
Mark44 said:
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

Vanadium 50 said:
you really should test that the read was successful in your code before going on.
yungman said:
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.
 
  • #28
Vanadium 50 said:
Thisand 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 address1[addr_L], address2[addr_L];
    //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: ";
        cin.getline(person.address1, addr_L);
        cout << " Enter address line 2: ";
        cin.getline(person.address2, addr_L);
        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
    {
        people.read(reinterpret_cast<char*>(&person), sizeof(person));
        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.
Compile error Listing 7.3.jpg
 
  • #29
yungman said:
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).
 
  • #30
pbuk said:
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
    {
        people.read(reinterpret_cast<char*>(&person), sizeof(person));
        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:
  • #31
yungman said:
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?

yungman said:
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.
 
  • Like
Likes Vanadium 50 and yungman
  • #32
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).
 
  • Like
Likes FactChecker, yungman and jtbell
  • #33
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
 
  • #34
yungman said:
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:
  • #35
yungman said:
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.
 
  • #36
jtbell said:
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.
 
  • #37
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
 
  • #38
yungman said:
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.
 
  • #39
yungman said:
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()?
pbuk said:
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 realize that "Michael" is only being read once.
 
  • #40
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.
    testFile.open("demofile.txt", ios::in);//open for read
    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
    {
        people.read(reinterpret_cast<char*>(&person), sizeof(person));
    } 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.
 
  • Like
Likes FactChecker
  • #41
If you look at the file demofile.txt is the number in it?
 
  • #42
FactChecker said:
If you look at the file demofile.txt is the number in it?
Yes. one single number 10.
 
  • #43
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/ )
 
  • #44
FactChecker said:
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
    std::cout << "[error reading]\n";

  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.
 
  • #45
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
    std::cout << "[error reading]\n";

  is.close();                  
  return 0;
}

I added comments which should help you out on this.

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:
  • Like
  • Informative
Likes yungman and FactChecker
  • #46
yungman said:
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
pbuk said:
You will not understand what is happening until you realize that "Michael" is only being read once.
Post #44
yungman said:
That's still doesn't explain why I read michael twice before the eof bit is set.
That name was NOT read twice!

Please read replies to your questions more carefully!

yungman said:
This is just like in my program
C++:
    do
    {
        people.read(reinterpret_cast<char*>(&person), sizeof(person));
    } 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
    testFile.read((char*)&val, sizeof(val));
    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

Read file.
The value   10   was read.

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:
  • Like
Likes pbuk
  • #47
Jarvis323 said:
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
    std::cout << "[error reading]\n";

  is.close();                 
  return 0;
}

I added comments which should help you out on this.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.
 
  • #48
Mark44 said:
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!

Please read replies to your questions more carefully!

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
    testFile.read((char*)&val, sizeof(val));
    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

Read file.
The value   10   was read.

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.
 
  • #49
Mark44 said:
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!

Please read replies to your questions more carefully!

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
    testFile.read((char*)&val, sizeof(val));
    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

Read file.
The value   10   was read.

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);
    people.read(reinterpret_cast<char*>(&person), sizeof(person));
    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";
        people.read(reinterpret_cast<char*>(&person), sizeof(person));
        index++;
        cout << " index = " << index << "    !people.eof() = " << !people.eof() << "\n\n";
    } while (!people.eof());

Something is inconsistent in this eof(), I just don't have the knowledge to dig deeper, so it's best to get on with this, put in my cheat sheet and hurry up to get onto Chapter 13 Classes.

thanks
 
  • #50
@yungman ##-## have you fully considered the fact that C++ is a strictly-typed language? That fact has important implications and consequences. Please read up on them.
 
Back
Top