2010-04-23 32 views
14

Chcę utworzyć mały kod w C++ z taką samą funkcjonalnością jak "tail-f": obserwuj nowe linie w pliku tekstowym i pokaż je na standardowym wyjściu.Implementacja "tail -f" w C++

Chodzi o to, aby mieć gwint, który monitoruje plik

Czy istnieje prosty sposób to zrobić bez otwierania i za każdym razem, zamykając plik?

+0

Można mieć twardy czas robi to w czystym C++ . Będziesz musiał użyć API specyficznego dla platformy. (Na początek, nie sądzę, że można otworzyć plik w C++ nie wyłącznie.) – sbi

+1

@sbi Nie sądzę, że standard C++ ma coś do powiedzenia na wyłączność. –

+1

Czy jest jakiś powód, dla którego nie można po prostu użyć tail -f? –

Odpowiedz

11

Po prostu czytaj dalej plik. Jeśli odczyt się nie powiedzie, nie rób nic. Nie trzeba go wielokrotnie otwierać i zamykać. Jednakże, o ile system operacyjny je zapewnia, o wiele skuteczniejsze będzie korzystanie z funkcji specyficznych dla systemu operacyjnego do monitorowania pliku.

+2

+1: Próba przeczytania do końca pliku, z którego do tej pory dotarłeś (dla pliku o rozsądnej długości), raz na sekundę jest w praktyce dość tania. Po prostu czytaj, aż do końca, a potem przespać się na chwilę i spróbować ponownie przeczytać. (Jeśli jesteś w systemie Windows, staraj się otwierać z właściwymi flagami współdzielenia, aby nie blokować innych pisarzy, co prawdopodobnie oznacza używanie natywnych wywołań We/Wy, a nie standardowych C++ ...) –

11
Zajrzyj na inotify w systemie Linux lub kqueue na Mac OS.

Inotify to podsystem jądra systemu Linux, który pozwala subskrybować zdarzenia w plikach i będzie raportował do aplikacji, gdy nawet wydarzy się na twoim pliku.

+0

Windows ma odpowiednik API do wysyłania powiadomień, gdy plik się zmienia. –

1

Przeczytałem to w one of Perl manuals, ale można je łatwo przetłumaczyć na standardowe C, które z kolei można przetłumaczyć na istream.

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. 

O ile pamiętam, fseek nazywa iostream::seekg. Więc powinieneś zrobić to samo: szukaj do końca pliku, następnie przespij się i poszukaj ponownie flagą ios_base::cur, aby zaktualizować koniec pliku i odczytać więcej danych.

Zamiast sleep, można za pomocą inotify, zgodnie z sugestią w the other answer, przespać się (blokować czytanie z pliku emulowanego, faktycznie) dokładnie do czasu, gdy plik zostanie zaktualizowany/zamknięty. Ale to jest specyficzne dla Linuksa i nie jest standardowym C++.

+0

kocham zamykanie "mamy nadzieję".Sticks dobrze razem z "to jest dziwne, ale to jest dobre, ponieważ jest dziwne" i wiele innych samoblokujących się realizacji, które są tak typowe dla perl ... –

+0

@Stefano - cóż, jest tu dobrze, ponieważ odnosi się do implementacji linii Perla czytanie pliku (''), a nie chodzi o to, jak działa 'fseek'. Mam nadzieję. –

1

Potrzebowałem to również wdrożyć, właśnie napisałem szybki hack w standardowym C++. Hack wyszukuje ostatniego 0x0A (znak wprowadzany wierszem) w pliku i wyprowadza wszystkie dane następujące po tym wprowadzeniu linii, gdy ostatnia wartość przesunięcia w linii stanie się większa. Kod jest tutaj:

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

} 
2

samo jak w https://stackoverflow.com/a/7514051/44729 chyba że kod poniżej używa getline zamiast getc i nie pominąć nowe linie

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

} 
+0

filesize Angela