2010-04-23 3 views
14

Я хочу создать небольшой код на C++ с той же функциональностью, что и «tail-f»: следить за новыми строками в текстовом файле и показывать их в стандартном выпуске.Реализовать «tail -f» в C++

Идея заключается в том, чтобы поток, который контролирует файл

Есть простой способ сделать это без открытия и закрытия файла каждый раз?

+0

Вы можете иметь трудное время, делая это в чистом C++ , Вам придется использовать некоторые API-интерфейсы для платформы. (Для начала я не думаю, что вы можете открыть файл на C++ не исключительно.) – sbi

+1

@sbi Я не думаю, что на стандарте C++ есть что сказать об исключительности. – 2010-04-23 07:54:08

+1

Есть ли причина, по которой вы не можете просто использовать tail -f? –

ответ

11

Просто продолжайте читать файл. Если чтение не выполняется, ничего не делайте. Нет необходимости многократно открывать и закрывать его. Тем не менее, вы обнаружите, что гораздо эффективнее использовать специфические для операционной системы функции для мониторинга файла, если ваша ОС предоставит их.

+2

+1: Попытка прочитать до конца файла, откуда вы до сих пор (для файла с разумной длиной), раз в секунду на практике довольно дешево. Вы только что прочитали, пока не дойдете до конца, затем спите секунду и попробуйте прочитать снова. (Если вы работаете в Windows, позаботьтесь о том, чтобы открыть с помощью правильных флагов обмена, чтобы вы не блокировали других авторов. Вероятно, это означает использование собственных вызовов ввода-вывода, а не стандартных C++ ...) –

11

Посмотрите на inotify на Linux или kqueue на Mac OS.

Inotify Подсистема ядра Linux, которая позволяет подписываться на события в файлах, и будет сообщать о вашем приложении, когда это произошло даже в вашем файле.

+0

Windows имеет эквивалентный API для отправки уведомлений при изменении файла. –

1

Я прочитал это в one of Perl manuals, но его легко перевести в стандарт C, который, в свою очередь, можно перевести на 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. 

Насколько я помню, fseek называется iostream::seekg. Поэтому вы должны в основном сделать то же самое: искать до конца файла, а затем снова спящий и искать с флагом ios_base::cur, чтобы обновить конец файла и прочитать еще несколько данных.

Вместо sleep, вы можете использовать inotify, как предложено в the other answer, для сна (на самом деле блокировать при чтении из эмулируемого файла), пока файл не будет обновлен/закрыт. Но это зависит от Linux и не является стандартным C++.

+0

любят закрытие «мы надеемся».Хорошо сочетается с «это странно, но это хорошо, потому что это странно» и многие другие саморазрушающие реализации, которые так типичны для perl ... –

+0

@Stefano - хорошо, это хорошо здесь, потому что это связано с реализацией Perl lin linise чтение файла (''), и не о том, как работает 'fseek'. Я надеюсь. –

1

Мне тоже нужно было реализовать это, я просто написал быстрый хак в стандартном C++. 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); 
    } 

} 
2

же, как и в https://stackoverflow.com/a/7514051/44729 за исключением того, что код ниже использует 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); 
    } 

} 
+0

filesize Angela

Смежные вопросы