Внедрить "tail -f" в С++
Я хочу создать небольшой код на С++ с той же функциональностью, что и "tail-f": следить за новыми строками в текстовом файле и показывать их в стандартном выводе.
Идея состоит в том, чтобы иметь поток, который контролирует файл
Есть ли простой способ сделать это, не открывая и не закрывая файл каждый раз?
Ответы
Ответ 1
Просто продолжайте читать файл. Если чтение не выполняется, ничего не делайте. Нет необходимости многократно открывать и закрывать его. Тем не менее, вы обнаружите, что гораздо эффективнее использовать специфические для операционной системы функции для мониторинга файла, если ваша ОС предоставляет им.
Ответ 2
Посмотрите inotify на Linux или kqueue в Mac OS.
Inotify - подсистема ядра Linux, которая позволяет вам подписываться на события в файлах и сообщать о своем заявлении, когда это произошло даже ваш файл.
Ответ 3
То же, что и в fooobar.com/questions/363906/..., за исключением того, что приведенный ниже код использует getline вместо getc и не пропускает новые строки
#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);
}
}
Ответ 4
Я читал это в одном из руководств Perl, но он легко переводится в стандарт C, который, в свою очередь, можно перевести на istream
с.
seek FILEHANDLE,POSITION,WHENCE
Sets FILEHANDLE 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.
Насколько я помню, fseek
называется iostream::seekg
. Поэтому вы должны в основном сделать то же самое: искать до конца файла, а затем снова спящий и искать с флагом ios_base::cur
для обновления конца файла и чтения еще нескольких данных.
Вместо sleep
ing вы можете использовать inotify, как предложено в другом ответе, чтобы спать (на самом деле блокировать при чтении из эмулируемого файла) пока файл не будет обновлен/закрыт. Но этот Linux-специфический и не является стандартным С++.
Ответ 5
Мне тоже нужно было реализовать это, я просто написал быстрый хак в стандартном С++. Hack ищет последний файл 0x0A (символ перевода строки) в файл и выводит все данные, следующие за этим возвратом строки, когда последнее значение строки возвращает большее значение. Код здесь:
#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);
}
}
Ответ 6
#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;
}