Question about getline with fstream

  • Thread starter yungman
  • Start date
In summary: This is why after the read operation in line 22, the position is now at 'r' rather than 'q'.I hope this helps.
  • #1
yungman
5,718
241
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
  • #3
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.
 
  • #4
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
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.
 
  • #6
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
  • #7
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
  • #8
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
  • #9
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

  • Chapter 12 notes.docx
    23.8 KB · Views: 235
  • #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.
 

Similar threads

  • Programming and Computer Science
Replies
32
Views
2K
  • Programming and Computer Science
Replies
6
Views
890
  • Programming and Computer Science
3
Replies
70
Views
3K
  • Programming and Computer Science
4
Replies
118
Views
6K
  • Programming and Computer Science
Replies
20
Views
1K
  • Programming and Computer Science
Replies
5
Views
885
  • Programming and Computer Science
Replies
5
Views
1K
  • Programming and Computer Science
Replies
33
Views
2K
Replies
10
Views
960
  • Programming and Computer Science
Replies
4
Views
4K
Back
Top