0

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);
  }
}
Community
  • 1
  • 1
nkint
  • 11,513
  • 31
  • 103
  • 174

1 Answers1

2

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

molbdnilo
  • 64,751
  • 3
  • 43
  • 82
  • According to the documentation of `std::basic_filebuf::open`, reopening should not behave as you describe but it should return `null` and keep working. – mostruash Dec 03 '14 at 09:20
  • @mostruash `std::basic_filebuf::open` returns null to indicate failure. – molbdnilo Dec 03 '14 at 09:24
  • @nkint Something has gone wrong with it, and it will not read anything any more. You never check whether any operations you do with it actually succeed. – molbdnilo Dec 03 '14 at 09:27
  • @molbdnilo Yeah, it fails because the file was already open and it failed to "reopen" it? I can't find where in the documentation it says that the stream would go into an error state. – mostruash Dec 03 '14 at 09:27
  • If using gcc check : https://gcc.gnu.org/onlinedocs/libstdc++/faq.html#faq.stream_reopening_fails – zoska Dec 03 '14 at 09:35
  • Oh, this one concerns a very old version of gcc... So maybe in your case http://www.cplusplus.com/reference/fstream/ifstream/open/ is right? – zoska Dec 03 '14 at 09:43
  • @mostruash I copied parts of the standard for you and added to the answer. – molbdnilo Dec 03 '14 at 09:45
  • @zoska Those concern opening a stream that has been closed. OP doesn't close the stream. – molbdnilo Dec 03 '14 at 09:46
  • So I have to open and close the file stream in each for loop? I thought to use a global variables to avoid the "heavy" computational cost of re-allocate the object each iteration.. – nkint Dec 03 '14 at 10:40
  • @molbdnilo there is no mentioning about closing in : `f the stream is already associated with a file (i.e., it is already open), calling this function fails.` – zoska Dec 03 '14 at 10:48