There are any number of problems with your code, starting with
the fact that just dumping bits to a file generally will not
give you anything useful that you can read back. The fact that
you need a reinterpret_cast
to use it should have tipped you
off. In addition:
You only open the file for output, and then try to read from
it. (Opening a file for output truncates it, so you've already
lost all of your previous data.)
I'm not sure what you think you're doing with while (!file.eof())
,
but it's surely not correct. If for some reason, the
file.read
fails without hitting the end of file, you'll end up
in an endless loop, for example.
And you're using the results of file.read
without verifying
that the function worked.
You close the file without being at the end. This truncates
a file open in output mode.
Same problems in the read loop of the second snippet. If the
file is empty, you'll still "read" one object, pushing the
default constructed temp
into products
.
Not knowing what Product
looks like, nor the initial contents
of the file, it's hard to say what is really happening. But
most likely:
You truncate the file with the open. It now has a length of
0.
Since file.tellg()
returns 0
, you never even try to read
it. (If you'd tried to read it, an error would have been set,
which would have made all successive operations no-ops.)
You then write the single element, and close the file.
The only thing I'm not too sure of: in this scenario, the file
actually does contain one element. So when you try to read it,
the first file.read
succeeds, probably without setting
eofbit
, since file.read
doesn't need any look ahead. And if
eofbit
isn't set, I would expect you to loop a second time,
and push the unmodified bits in temp
into products
a second
time.
EDIT:
FWIW: if we assume that you're in the very restricted case
where just writing the data bits to the disk is valid (which
normally means that you'll be rereading them later in the same
process, but never from a different processs), and that the
id
can never be -1 in a valid Product
, what you probably
want to do is:
Product temp;
temp.setId( -1 ); // This sort of thing should really be handled by a constructor
std::fstream file( "cigartettes.dat", ios::out | ios::in | ios::binary );
while ( file.read( reinterpret_cast<char*>( &temp ), sizeof(temp) && temp.getId() != getId() ) {
}
if ( file.gcount() != 0 ) {
// Error somewhere, we ended up reading a partial record
} else if ( temp.getId() == getId() ) {
file.seekp( -static_cast<int>( sizeof(temp) ) );
} else {
file.clear();
file.flush();
}
file.write( reinterpret_cast<char const*>( this ), sizeof(*this) );
file.close();
if ( !file ) {
// Something went wrong somewhere...
}
Several comments:
Opening in both input and output is necessary. Opening only
in output means that 1) the file will be truncated, and 2) any
attempt to read it will fail.
file.read
will fail if it cannot read the correct number of
bytes. If it fails, it might have read some bytes anyway (and
overwritten the id field in Product
, and left the current
pointer at some position which isn't a modulo of your object
size). So you should check for this using the value from
file.gcount()
(which returns the number of bytes read by the
last unformatted read operation—in the case of the read
you're doing, this can only be different from sizeof(Product)
if the read failed.
When specifying a negative value to seek backwards: you have
to convert the results of sizeof
to a signed type before doing
the -
. Otherwise, you'll end up with some astronomical
positive value, which will cause you to try to seek beyond the
end of file (which will fail).
When the read fails, and the number of bytes read is 0, you've
reached the end of file. And set the failbit
, which will
cause all future operations to fail. So we have to clear the
error if we're going to write to extend the data. (If we
haven't reached end of file, of course, there's nothing to
clear.)
When doing bidirectional input, after a read, you must execute
either a seek or a flush before a write. (Don't ask me why;
it's just what the standard says.)
Finally, it's good practice to verify the status of the file
after closing, when all buffers have been fully flushed and
passed to the OS. If for some reason, a write has failed
somewhere, you want to know about it, to inform the user that
the file that was output is corrupt.
I might add that the usual way of modifying just one record in
a file is to copy the file to a new file, replacing or appending
the changed record, and then delete the old file and rename the
new. Trying to modify a file, as you are doing, can mean that
you loose all of the data if something goes wrong.