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

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

Related

std::getline is reading line where specified delimiter is not present?

I want to print each object in console from the array of the following string (stored in a file):
{ beforechars [{Object1},{Object2},{Object3}] afterchars }
I'm doing it as follows:
std::ifstream is("content.txt");
std::getline(is, content, '[');
while (std::getline(is,content,'{')) {
std::getline(is,content,'}');
std::cout << content << std::endl;
}
in.close();
But i am getting this output:
Object1
Object2
Object3
] afterchars }
My understanding is that after Object3 iteration, the ifstream should have "}] afterchars }" and the while's guard shouldn't be true because there isn't any '{' char... Am i right? Where is the mistake?
The while condition doesn't work as you expect: getline() will read successfully until it reaches an '{' or to the end of the file if not.
So what happens here ?
when you've displayed Object3 your position in the stream is after the closing '}'.
The getline() in the while condition will read all the remaining of the file into content as it encounters no '{'. As it could read something successfully, the condition is evaluated to true.
the getline() within the while block then fails to read anything, so content will remain unchanged. The stream is then in fail status. No subsequent operation will succeed until you clear this state. But nothing visible happens for now in your code.
after displaying this last result, the next loop condition will fail.
Simple workaround:
A very easy workaround would be to keep the current position in the stream before looking for '{', and in case it was not found, go back to this position. Attention: this way of parsing files is not so nice from point of view of performance, but it's ok for small files.
std::getline(is, content, '[');
auto pos = is.tellg(); // read current position
while (std::getline(is,content,'{') && !is.eof()) {
std::getline(is,content,'}');
pos = is.tellg(); // update position before iterating again
std::cout << content << std::endl;
}
is.seekg(pos); // and get back to last position
The trick here is that if '{' is not found, after the getline() the stream is not yet in fail state, but eof() is already true. We can then end the loop and go back to the last recorded position.
Online demo
std::getline reads characters until delimiter (consuming it) or until the end of the stream. It sets failbit on stream only if there were no character consumed (called on empty/invalid stream).
So your loop will terminate only when stream is empty.
Streams interface allows only to see next character, there is no way to scan input and do read if there specific character present.
If you need random access to characters, you need to read input in string and then parse it (with regular expressions or something else.)

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.

File Stream failing on read after seeking to end of file

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.

c++ is it safe to unget on fstreams

Suppose input.txt is 1 byte text file:
std::ifstream fin("input.txt", std::ios::in);
fin.get(); // 1st byte extracted
fin.get(); // try to extract 2nd byte
std::cout << fin.eof(); // eof is triggered
fin.unget(); // return back
std::cout << fin.eof(); // eof is now reset
fin.get(); // try to extract 2nd byte, eof assumed
std::cout << fin.eof(); // no eof is triggered
Seems like unget() breaks eof flag triggering also it breaks file pointers. Am I doing something wrong?
eof is not set, but neither is good. The stream is ignoring operations because it's in a failure mode.
I cannot recall what unget is supposed to do after EOF, but unget goes right back into failure if I use clear to allow a retry.
http://ideone.com/JkDrwG
It's usually better to use your own buffer. Putback is a hack.

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