Printing the last item of a structure twice

R

Rafael Anschau

Hi,

The following program reads a structure into a file 3 times, but when
I tell
it to print them all, it prints the last one twice. I apreciate any
help on what
am I doing to cause this.

Thank you,

Rafael

#include <string.h>
#include <string>
#include<iostream>

using namespace std;


int _tmain(int argc, _TCHAR* argv[])
{
char nf[]="1.tst";
string str="Hello world";
FILE *fp;


if ((fp=fopen(nf,"w+"))==NULL) {
std::cout<<"Erro nao abriu";
int a;
cin>>a;
exit(1);
}
typedef struct endereco {
string rua;
int n;


} ender;

ender meu,teu;

int i;

rewind(fp);
for(i=0;i<3;i++) {
cout<<"Nome da rua:\n";
cin>>meu.rua;
cout<<"Numero do ap:\n";
cin>>meu.n;



fwrite(&meu,sizeof(ender),1,fp);

}

cout<<"Listando\n";

rewind(fp);
while(!feof(fp)) {
fread(&teu,sizeof(ender),1,fp);
cout<<teu.rua<<"\n";
cout<<teu.n<<"\n";
}

int a;
cin>>a;

}
 
J

Jonathan Lee

The following program reads a structure into a file 3 times

Yes, but that's not actually what you want it to do. You want it to
write the _value_ of rua, and the int. But that's not what it's doing.

If you check sizeof(string) you will find that it is always the same
regardless of the contents. On my machine, that's 4 bytes, probably
because it is only a wrapper around a pointer.

So when you do this:

fwrite(&meu, sizeof(ender), 1, fp);

you are only writing a pointer ("rua") and the int ("n"). You're not
writing the string that "rua" is pointing to.

Depending on your inputs, that pointer stored by "rua" might not
actually change. So you write out the same pointer 3 times. Then you
read that same pointer back 3 times and they all point to the same
thing: the last string you've read from the keyboard.

You really need a way to serialize the object (hint: Google "serialize
C++") and a way to recover it. Doing it the way you have is a bad idea
-- as you've seen it doesn't work.

--Jonathan
 
R

Rafael Anschau

Actually the problem was I had to refine my definition for feof: It
only returns trues after it tries to pass eof, not when it reaches it.

But thanks anyway,

Rafael
 
J

Jerry Coffin

[ ... ]
while(!feof(fp)) {

Your problem is right here. The problem is that eof on a stream isn't
detected until AFTER you attempt a read and you're at the end of the
end of the file, and the attempted read fails. When the read fails at
the end of the file, what was previously read will still be there,
but you won't have detected the end of the file yet, so you'll
process it again. Then the code above will detect that the previous
read failed and quit trying to read any more.
 
R

red floyd

Jerry said:
[ ... ]
while(!feof(fp)) {

Your problem is right here. The problem is that eof on a stream isn't
detected until AFTER you attempt a read and you're at the end of the
end of the file, and the attempted read fails. When the read fails at
the end of the file, what was previously read will still be there,
but you won't have detected the end of the file yet, so you'll
process it again. Then the code above will detect that the previous
read failed and quit trying to read any more.

What Jerry said. See FAQ 15.5

http://parashift.com/c++-faq-lite/input-output.html#faq-15.5
 
J

James Kanze

Actually the problem was I had to refine my definition for
feof: It only returns trues after it tries to pass eof, not
when it reaches it.

That's certainly one problem, but Jonathan is also right. You
can't use fread and fwrite (nor istream::read and
istream::write) on the structure you've defined. If it works,
it's only by chance. In general, binary dumps of anything but
char only work within the same process---you can't reread the
data from code compiled with a different compiler, or sometimes
different version of the same compiler or with different
compiler options. And even within the same process, you're
restricted to PODs without pointers.
 
R

Rafael Anschau

"Yes, but that's not actually what you want it to do. You want it to
write the _value_ of rua, and the int. But that's not what it's doing.
"

How come ? I can read the file after I quit and come back(I just need
to set open to a+ for instance) If the data is not there, then where
is it ?

Thanks,

Rafael
 
J

Jonathan Lee

How come ? I can read the file after I quit and come back(I just need
to set open to a+ for instance) If the data is not there, then where
is it ?

First I recommend trying this: add the following line before
you write to the file:

cout << "Writing " << sizeof(ender) << " bytes" << endl;

It will print the same thing for every record, on my system that's
8 bytes. It's 8 bytes if I enter a 2 letter string for the street
name, and it's 8 bytes if I enter 40 letters for the street name.
When it's done, "1.tst" is always 24 bytes. So if I've entered
three 40 letter names, how could the data possibly be there?

Now to answer your question. What fwrite is doing is writing
the 8 bytes (sizeof(ender)) located at the address &meu. Of course,
this contains an ender structure, which is roughly

struct ender {
string rua;
int n;
}

Now rua, the string object, is just a pointer to a char* buffer
(a bit of a simplification, but good enough for this discussion).
So in reality you have this:

struct ender {
char* rua; // the only data a string object "contains"
int n;
}

And rua only contains a pointer to the actual string data. So
this is what you get when you write it out to the file.

As a concrete example, suppose I type in the street name as
"ReallyLongStreetName" and the number as 9. When you read the
value into rua, it will allocate a char* to hold the data and
store that address inside the string object. Let's say that
address is 0x40302010. Then 'meu' will hold

0x40302010 // string rua;
0x00000009 // int n;

When you write out the file you will get something like

0x40, 0x30, 0x20, 0x10, 0x00, 0x00, 0x00, 0x09

in 1st.tst, which has nothing to do with "ReallyLongStreetName"

When you read it back you get the same address back and it
just happens to contain the same string, leading you to
think that it's working, when it isn't.

What you need to do is serialize the object, as I said before.
One way to do this would be:

write the string length as a number
write the C-string value of rua, pointed to by rua.c_str()
write the street number

One final note: writing numbers is not portable. There is the
matter of "endianness", and also sizeof(int). But maybe convince
yourself of the above first...

--Jonathan
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,995
Messages
2,570,233
Members
46,820
Latest member
GilbertoA5

Latest Threads

Top