开发者

Implement "tail -f" in C++

I want to create a small code in C++ with the same functionality as "tail-f": watch for new lines in a text file and show them in the standard output.

The idea is to have a thread that moni开发者_Go百科tors the file

Is there an easy way to do it without opening and closing the file each time?


Have a look at inotify on Linux or kqueue on Mac OS.

Inotify is Linux kernel subsystem that allows you to subscribe for events on files and it will report to your application when the even happened on your file.


Just keep reading the file. If the read fails, do nothing. There's no need to repeatedly open and close it. However, you will find it much more efficient to use operating system specific features to monitor the file, should your OS provide them.


Same as in https://stackoverflow.com/a/7514051/44729 except that the code below uses getline instead of getc and doesn't skip new lines

#include <iostream>
#include <string>
#include <fstream>
#include <sstream>

using namespace std;

static int last_position=0;
// read file untill new line
// save position

int find_new_text(ifstream &infile) {

   infile.seekg(0,ios::end);
   int filesize = infile.tellg();

   // check if the new file started
   if(filesize < last_position){
      last_position=0;
   }  
   // read file from last position  untill new line is found 

   for(int n=last_position;n<filesize;n++) {

      infile.seekg( last_position,ios::beg);
      char  test[256]; 
      infile.getline(test, 256);
      last_position = infile.tellg();
      cout << "Char: "  << test <<"Last position " << last_position<<  endl;
      // end of file 
      if(filesize == last_position){
        return filesize;
      } 

  }

  return 0;
}


int main() {

  for(;;) {
    std::ifstream infile("filename");
    int current_position = find_new_text(infile);
    sleep(1);
  } 

} 


I read this in one of Perl manuals, but it is easily translated into standard C, which, in turn, can be translated to istreams.

   seek FILEHANDLE,POSITION,WHENCE
      Sets FILEHANDLE's position, just like the "fseek" call of
      "stdio".  
       <...>
       A WHENCE of 1 ("SEEK_CUR") is useful for not moving the file 
       position:

           seek(TEST,0,1);

       This is also useful for applications emulating "tail -f".  Once
       you hit EOF on your read, and then sleep for a while, you might
       have to stick in a seek() to reset things.  The "seek" doesn't
       change the current position, but it does clear the end-of-file
       condition on the handle, so that the next "<FILE>" makes Perl
       try again to read something.  We hope.

As far as I remember, fseek is called iostream::seekg. So you should basically do the same: seek to the end of the file, then sleep and seek again with ios_base::cur flag to update end-of-file and read some more data.

Instead of sleeping, you may use inotify, as suggested in the other answer, to sleep (block while reading from an emulated file, actually) exactly until the file is updated/closed. But that's Linux-specific, and is not standard C++.


I needed to implement this too, I just wrote a quick hack in standard C++. The hack searches for the last 0x0A (linefeed character) in a file and outputs all data following that linefeed when the last linefeed value becomes a larger value. The code is here:

#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("testfile");
    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);
  }

}


#include <iostream>
#include <fstream>
#include <string>
#include <list>
#include <sys/stat.h> 
#include <stdlib.h>

#define debug 0

class MyTail
{
private:
std::list<std::string> mLastNLine;
const int mNoOfLines;
std::ifstream mIn;

public:

explicit MyTail(int pNoOfLines):mNoOfLines(pNoOfLines) {}

const int getNoOfLines() {return mNoOfLines; }

void getLastNLines();

void printLastNLines();

void tailF(const char* filename);

};

void MyTail::getLastNLines() 
{
    if (debug) std::cout << "In: getLastNLines()" << std::endl;
    mIn.seekg(-1,std::ios::end);
    int pos=mIn.tellg();
    int count = 1;

    //Get file pointer to point to bottom up mNoOfLines.
    for(int i=0;i<pos;i++)
    {
        if (mIn.get() == '\n')
            if (count++ > mNoOfLines)
                break;
        mIn.seekg(-2,std::ios::cur);
    }

    //Start copying bottom mNoOfLines string into list to avoid I/O calls to print lines
    std::string line;
    while(getline(mIn,line)) {
        int data_Size = mLastNLine.size();
        if(data_Size >= mNoOfLines) {
            mLastNLine.pop_front();
        }
        mLastNLine.push_back(line);
    }

    if (debug) std::cout << "Out: getLastNLines()" << std::endl;
}

void MyTail::printLastNLines()
{    
     for (std::list<std::string>::iterator i = mLastNLine.begin();  i !=         mLastNLine.end(); ++i)
     std::cout << *i << std::endl;
}

void MyTail::tailF(const char* filename)
{
    if (debug) std::cout << "In: TailF()" << std::endl;

    int date = 0;
    while (true) {
        struct stat st;
        stat (filename, &st);
        int newdate = st.st_mtime;
        if (newdate != date){
            system("@cls||clear");
            std::cout << "Print last " << getNoOfLines() << " Lines: \n";
            mIn.open(filename);
            date = newdate;
            getLastNLines();
            mIn.close();
            printLastNLines();
        }
    }
    if (debug) std::cout << "Out: TailF()" << std::endl;        
}

int main(int argc, char **argv)
{
    if(argc==1) {
        std::cout << "No Extra Command Line Argument Passed Other Than Program Name\n"; 
        return 0;
    }

    if(argc>=2) {
        MyTail t1(10);
        t1.tailF(argv[1]);
    }
    return 0;
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜