Question about getline with fstream

  • Thread starter yungman
  • Start date
  • #1
4,939
110
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
 

Answers and Replies

  • #3
4,939
110
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.
 
  • #4
4,939
110
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:
  • #5
pbuk
Science Advisor
Gold Member
1,669
576
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'...
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.

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.

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.
 
  • #6
34,140
5,762
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
  • #7
34,140
5,762
You wrote this in post #1.
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
  • #8
pbuk
Science Advisor
Gold Member
1,669
576
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
  • #9
4,939
110
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
4,939
110
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
34,140
5,762
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
FactChecker
Science Advisor
Gold Member
5,779
2,149
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
4,939
110
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
4,939
110
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
34,140
5,762
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.
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
4,939
110
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
4,939
110
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
4,939
110
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
34,140
5,762
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.
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.
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
34,140
5,762
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
4,939
110
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
Vanadium 50
Staff Emeritus
Science Advisor
Education Advisor
2019 Award
25,364
8,544
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
4,939
110
@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
4,939
110
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
FactChecker
Science Advisor
Gold Member
5,779
2,149
@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.
 

Related Threads on Question about getline with fstream

  • Last Post
Replies
0
Views
2K
  • Last Post
Replies
17
Views
10K
Replies
9
Views
20K
Top