Java - RandomAccessFile (эмуляция хвостовой функции Linux)
В Java IO реализация unix/linux "tail -f" имеет похожую проблему; но решение не подходит для файлов журналов, которые генерируют около 50-100 строк в секунду.
У меня есть алгоритм, который имитирует функциональность хвоста в Linux. Например,
File _logFile = new File("/tmp/myFile.txt");
long _filePtr = _logFile.length();
while (true)
{
long length = _logFile.length();
if (length < _filePtr)
{
// means file was truncated
}
else if (length > _filePtr)
{
// means something was added to the file
}
// we ignore len = _filePtr ... nothing was written to file
}
Моя проблема, когда: "что-то было добавлено в файл" (ссылаясь на оператор else if ()).
else if (length > _filePtr)
{
RandomAccessFile _raf = new RandomAccessFile(_logFile, "r");
raf.seek(_filePtr);
while ((curLine = raf.readLine()) != null)
myTextPane.append(curLine);
_filePtr = raf.getFilePointer();
raf.close();
}
Программа блокируется в то время как ((curLine = raf.readLine()).... после 15 секунд работы! (Примечание: программа работает в течение первых 15 секунд).
Похоже, что raf.readLine() никогда не достигает NULL, потому что я считаю, что этот файл журнала пишется так быстро, что мы заходим в цикл "бесконечный кот и мышь".
Какой лучший способ подражать хвосту Linux?
3 ответа
Я думаю, что вам лучше всего получить блок байтов в зависимости от длины файла, а затем освободить файл и проанализировать ByteArrayInputStream (вместо попытки чтения непосредственно из файла).
Поэтому используйте RandomAccessFile#read(byte[]) и размер буфера, используя длину возвращаемого файла. Вы не всегда будете показывать точный конец файла, но этого следует ожидать с помощью такого алгоритма опроса.
Кроме того, этот алгоритм ужасен - вы выполняете операции ввода-вывода в сумасшедшем узком цикле - вызовы File#length() будут блокироваться, но не очень сильно. Ожидайте, что эта рутина поставит ваше приложение на колени с точки зрения процессора. У меня не обязательно есть лучшее решение для вас (ну, собственно, да, исходное приложение записывает в поток, а не в файл, но я понимаю, что это не всегда возможно).
В дополнение к вышесказанному, вы можете захотеть ввести задержку опроса (спите поток на 100 мс в каждом цикле - мне кажется, что вы выводите на графический интерфейс - задержка в 100 мс никому не повредит, и значительно улучшит производительность качели).
хорошо - финальная фраза: вы настраиваете компонент Swing, исходя из того, что (я надеюсь) код не работает на EDT. Используйте SwingWorker#invokeLater() для обновления вашей текстовой панели.
Похоже, я нашел проблему и создал решение.
Под утверждением else if:
while ((curLine = raf.readLine()) != null)
myTextPane.append(curLine);
Это была проблема. метод append(String) в myTextPane (который является производным классом JTextPane) вызывал setCaretPosition() в каждой строке append, которая ПЛОХАЯ!!
Это означало, что setCaretPosition() вызывался 50-100 Гц, пытаясь "прокрутить вниз". Это вызвало блокировку интерфейса.
Простым решением было создать класс StringBuffer и добавить "curLine", пока raf.readLine() не прочитает ноль.
Затем добавьте StringBuffer и вуаля... больше не нужно блокировать функцию setCaretPosition()!
Спасибо Кевину за то, что он направил меня в правильном направлении.
Вы всегда можете выполнить программу tail:
BufferedReader in = new BufferedReader(new InputStreamReader(
Runtime.getRuntime().exec("tail -F /tmp/myFile.txt").getInputStream()));
String line;
while ((line = in.readLine()) != null) {
// process line
}