I'm trying to write a simple tail program in C++. I've tried the example from this solution and it works like a charm.
Then, I tried to make the ifstream
as global. Now the code does not work anymore and nothing is showed if I edit the file.
Why this behaviour? I read the manual of ifstream::open
and I don't see any kind of error but the code does not work:
Opens the file identified by argument filename, associating it with the stream object, so that input/output operations are performed on its content. Argument mode specifies the opening mode.
Here is the non-working code:
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
int find_last_linefeed(ifstream &infile) {
infile.seekg(0,ios::end);
int filesize = infile.tellg();
for(int n=1;n<filesize;n++) {
infile.seekg(filesize-n-1,ios::beg);
char c;
infile.get(c);
if(c == 0x0A) return infile.tellg();
}
}
ifstream infile;
int main() {
int last_position=-1;
for(;;) {
infile.open("/Users/alberto/tmp/test");
int position = find_last_linefeed(infile);
if(position > last_position) {
infile.seekg(position,ios::beg);
string in;
infile >> in;
cout << in << endl;
}
last_position=position;
sleep(1);
}
}
Here is the working code:
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
int find_last_linefeed(ifstream &infile) {
infile.seekg(0,ios::end);
int filesize = infile.tellg();
for(int n=1;n<filesize;n++) {
infile.seekg(filesize-n-1,ios::beg);
char c;
infile.get(c);
if(c == 0x0A) return infile.tellg();
}
}
int main() {
int last_position=-1;
for(;;) {
ifstream infile;
infile.open("/Users/alberto/tmp/test");
int position = find_last_linefeed(infile);
if(position > last_position) {
infile.seekg(position,ios::beg);
string in;
infile >> in;
cout << in << endl;
}
last_position=position;
sleep(1);
}
}
Since you open
inside the loop, the stream will enter an error state on the second iteration, and keep failing after that.
Move
infile.open("/Users/alberto/tmp/test");
outside the loop, or define infile
like this and don't use open
at all:
ifstream infile("/Users/alberto/tmp/test");
The best alternative is to not use a global variable at all, as there's no reason for it here.
Also, find_last_linefeed
fails to return anything if the file doesn't contain a linefeed, which is undefined.
—-
Regarding ifstream::open
, from the standard (27.9.1.9):
Calls rdbuf()->open(s, mode | ios_base::in). If that function does not return a null pointer calls clear(), otherwise calls setstate(failbit)
and basic_filebuf::open
(27.9.1.4):
If is_open() != false, returns a null pointer