File Stream failing on read after seeking to end of file - c++

I need to read the last 64kb from a large file, but after seeking to the end of the file, then back 64kb, all reads on the file fail.
Here's a snippet:
string filename = "test.txt";
ifstream::streampos pos; // used to keep track of position in file
ifstream fs;
fs.open(filename, ios::binary);
fs.seekg(0, ios::end);
pos = fs.tellg();
// ....
fs.seekg(-16, ios::cur);
char buff[16];
fs.read(buff, 16);
// ...
fs.close();
The problem is on fs.read() the eofbit, failbit, and badbit are set and the file stream becomes unusable. It is worth noting that the file I'm reading from is being continuously appended to. Also, sometimes this approach works and sometimes it doesn't, which makes debugging difficult.
Is there any reason why this would be happening?

Your problem is that you call tellg after seeking the end of file. tellg causes the pointer to advance invoking the eof state on the stream: Here is the issue:
fs.open(filename, ios::binary);
fs.seekg(0, ios::end);
pos = fs.tellg(); // <== causes eof on stream
Your next call to fs.seekg should clear the eof state, but it is probably better to call fs.clear() and check the return to insure it succeeds.

Related

File handling not working with fstream after reaching eof?

On your suggestion i have changed the code as you suggested but still problems are there when ios::out is replaced with ios::ate nothing is written in the file(Writing does not work). Is there any way to check that if the next bit is eof rather than reading it and then checking it? as suggested by you.And sometimes when i do file handling it shows the position of file pointer to be -1 what could that mean???
Code:
int main ()
{
char p[80];
fstream file("text1.txt",ios::out|ios::in); //if ios::ate is added here it results into infinite loop
cout<<"Starting position of the file is "<<file.tellg()<<endl;
getch();
if(file.is_open())
cout<<"file is open\n";
else
cout<<"file is not open\n";
getch();
file.seekp(0);
while(file>>p)
{
cout<<p<<endl;
}
file.clear();
cout<<"\nThe current position of the file pointer is "<<file.tellg()<<endl;
file.seekp(0);
if(file.eof())
cout<<"\n the eof\n";
while(file>>p)
{
cout<<p<<endl;
}
file.close();
return 0;
}
Output:
Starting position of the file is 0
file is open
Hello
man
how
are
you
The current position of the file pointer is 21
Hello
man
how
are
you
With this kind of reading from file reaching end-of-file causes setting both eof and failbit. Failbit is setted because creating your read loop with file.eof() condition doesn't indicate that next read will be the end of the stream. It just states that we didn't reach eof yet, so with:
while(file.eof())
{
file >> p;
}
It's possible that last read will be eof only, and we'll work with uninitialised data. IF this happens no characters will be extracted inside p and both eof and fail flags will be set.
When working with c++98 need to reset failbit to false by using:
file.clear();
To avoid bad readings situation you should extract characters from file inside while condition: while(file >> p). I recommend this or this questions on stack overflow.
So proper C++98 code should look like this:
while(file >> p)
{
std::count << p << std::endl;
}
file.clear();
file.seekp(0);
while(file >> p)
{
std::count << p << std::endl;
}
file.close();
I tested it couple of times on Visual Studio 2013 and it worked everytime.
Considering ios::ate mode:
ios::out, ios::in are modifiers that states how do we open file in question. If you want to read something from file you need to use ios::out flag, and for writing you need to use ios::in.
On the other hand ios::ate just tells compiler to open file and immediately go to the end of file. So if you substitute ios::out with ios::ate writing would be impossible, and program will rise failflag on file << "Hello...";.
And if you just want to append data, but read from the beginning of file you should use ios::app instead, because it tells to seek eof before each write.

c++ this to write object into binary file

I've some trouble with this pointer in c++ function.
I would write my class into binary file, so I write this function member
void Product::saveProducts(){
fstream file;
Product temp;
temp.setId(-1);
bool flag = false;
file.open("cigarettes.dat", ios::out | ios::binary);
if(file.is_open()){
file.seekg(0, ios::end);
if(file.tellg()!=0){
file.seekg(0, ios::beg);
while(!file.eof()){
file.read(reinterpret_cast<char *>(&temp), sizeof(temp));
if(temp.getId() == this->getId()){
flag=true;
file.seekp(-sizeof(temp),ios::cur);
file.write(reinterpret_cast<char *>(this), sizeof(temp));
break;
}
}
}
if(!flag){
file.write(reinterpret_cast<char *>(this), sizeof(temp));
}
}
file.flush();
file.close();
}
But when I try to retrieve my stored object with another function member:
list<Product> Product::loadProducsts(){
fstream file;
Product temp;
list<Product> products;
file.open("cigarettes.dat", ios::in | ios::binary);
if(file.is_open()){
while(!file.eof()){
file.read(reinterpret_cast<char *>(&temp), sizeof(temp));
products.push_front(temp);
}
}
file.close();
return products;
}
My array is filled with only one object empty. What's the problem?
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.

How to check if there isn't data in file to read

std::fstream fin("emptyFile", std::fstream::in);
std::cout << fin.eof() << std::endl;
This prints 0. So using eof function I can't check if file is empty. Or after reading some data I need to check if there is no more data in it.
There are two ways to check if you "can read something" from a file:
Try to read it, and if it fails, it wasn't OK... (e.g fin >> var;)
Check the size of the file, using fin.seekg(0, ios_base::end); followed by size_t len = fin.tellg(); (and then move back to the beginning with fin.seekg(0, ios_base::beg);)
However, if you are trying to read an integer from a text-file, the second method may not work - the file could be 2MB long, and still not contain a single integer value, because it's all spaces and newlines, etc.
Note that fin.eof() tells you if there has been an attempt to read BEYOND the end of the file.
eof() gives you the wrong result because eofbit is not set yet. If you read something you will pass the end of the file and eofbit will be set.
Avoid eof() and use the following:
std::streampos current = fin.tellg();
fin.seekg (0, fin.end);
bool empty = !fin.tellg(); // true if empty file
fin.seekg (current, fin.beg); //restore stream position

Can't read file more than once in c++

In my assignment i have a problem with reading a file. See the following code segment.
std::string data;
std::ifstream fileRead;
fileRead.open("a.txt");
while (fileRead >> data)
{
long a = fileRead.tellg();
fileRead.seekg (a+1, ios::beg);
std::string check;
//some code here
while (fileRead >> check)
{
//some code here
}
fileRead.seekg (a+1, ios::beg);
}
I have to check how many same words are in the file. My logic is, i read a word and keep it in data. Then i continue reading after that word using fileRead.seekg (a+1, ios::beg); Then i compare each of the words in the file.
After checking whole file i again put my fileobject to next word using this line fileRead.seekg (a+1, ios::beg); But this line is not working. I mean first while loop just work once. Someone please help me out here.
By the way, i am totally new in file operation. So my logic or concept may not be correct in that case tell me what is the right way to do this? But i need to know why first while loop ends just after reading one word?
When you read file as while(fileRead >> data), the a failure flag is set for the stream object, before exiting the loop. In fact, it is set that is why it exits the loop. You need to clear this failure flagas:
fileRead.clear(); //clear the failure flag
fileRead.seekg (0, ios::beg);
//now read

Resetting the End of file state of a ifstream object in C++

I was wondering if there was a way to reset the eof state in C++?
For a file, you can just seek to any position. For example, to rewind to the beginning:
std::ifstream infile("hello.txt");
while (infile.read(...)) { /*...*/ } // etc etc
infile.clear(); // clear fail and eof bits
infile.seekg(0, std::ios::beg); // back to the start!
If you already read past the end, you have to reset the error flags with clear() as #Jerry Coffin suggests.
Presumably you mean on an iostream. In this case, the stream's clear() should do the job.
I agree with the answer above, but ran into this same issue tonight. So I thought I would post some code that's a bit more tutorial and shows the stream position at each step of the process. I probably should have checked here...BEFORE...I spent an hour figuring this out on my own.
ifstream ifs("alpha.dat"); //open a file
if(!ifs) throw runtime_error("unable to open table file");
while(getline(ifs, line)){
//......///
}
//reset the stream for another pass
int pos = ifs.tellg();
cout<<"pos is: "<<pos<<endl; //pos is: -1 tellg() failed because the stream failed
ifs.clear();
pos = ifs.tellg();
cout<<"pos is: "<<pos<<endl; //pos is: 7742'ish (aka the end of the file)
ifs.seekg(0);
pos = ifs.tellg();
cout<<"pos is: "<<pos<<endl; //pos is: 0 and ready for action
//stream is ready for another pass
while(getline(ifs, line) { //...// }